2016-02-03 57 views
1

是否返回数组内容的副本而不是直接引用改进封装的类?我想知道这是否被认为是最佳做法。Java封装&数组

+0

我甚至不会使用数组,但是,复制/克隆数组可以防止其他类操作您自己的数组。但是请注意,如果使用浅层克隆,那么数组中的对象不会被复制。他们仍然可能是可变的。 – Tom

回答

3

如果我正确理解你,你正试图封装一个集合。如果您提供直接引用,则不会为该集合封装。用户可以对集合进行任何操作 - 甚至将其设置为空。如果您向用户提供集合的深层副本,那么当他们修改该副本时,他们不会修改班级中的集合。

最好的做法是封装整个集合,永不给用户对集合的引用。相反,他们将通过类方法与它进行交互。如果他们需要迭代整个集合,那么最好在集合上返回一个Iterator,它将为它提供只读访问权限。

例如说我们有一个BookCollection类,它有一个私有List作为数据成员。如果我们希望代码的使用者能够添加和删除书籍等,我们将提供方法让他们这样做。

这里是我的示例代码:

public class BookCollection { 
    private List<Book> books; 

    public BookCollection() { 
     this.books = new ArrayList(); 
    } 

    public void addBook(Book book) { 
     this.books.add(book); 
    } 

    public void removeBook(Book book) { 
     this.books.remove(book); 
    } 

    public Book get(int index) { 
     return this.books.get(index); 
    } 

    // etc. 

    public Iterator<Book> getIterator() { 
     return this.books.iterator(); 
    } 
} 
+1

我同意这个想法。你也可以考虑在BookCollection上实现Iterable接口来支持每一个接口。 – XORshift

0

是否返回数组,而不是直接 参考的内容的拷贝提高类的封装?我想知道 是否被认为是最佳做法。

数据隐藏是守护你的对象领域的对抗,除非允许的,由程序员控制的其他对象的访问的做法。在没有数据隐藏的情况下将状态封装在对象中将会困难得多。 Java有两大类的值类型:基本类型(只有它们的值)和引用类型(具有与其值不同的标识)。

信息隐藏和原始类型

当你有一个实例字段是不可变的,一个int例如,更容易维护的数据隐藏,因为返回int字段的值没有提供任何机制来用户你的目标是改变它的价值。换句话说,当您将不可变的原始类型返回给客户端代码时,您无需担心防御性复制。

public class MyClass { 
    private int myField1; 

    public MyClass(int initVal) { this.myField = initVal; } 

    public int getValue() { 
     return myField1; // safe because ints are immutable, primitive types 
    } 
    : 
    : 
} 

信息隐藏和引用类型

大多数类型Java中,包括许多程序员写自己,是引用类型(即它们包含身份除了它们的值的类)。当程序员使用变量作为引用类型时,该变量确实包含一个地址到内存中一个对象实例所在的位置。引用类型的变量实际上并不包含该类型的值。

返回引用类型可以允许您的代码的用户使用引用来访问和更改对象的字段,即使您不打算允许它们(如果引用指向可变对象)。

public class MyClass2 { 
    private int[] myField2; 

    public MyClass2(int[] initVal) { this.myField = initVal; } 

    public int[] getValue() { 
     return myField2; // not safe because int[] arrays are objects that 
          // are mutable reference types. Anyone can use 
          // the returned reference to modify the array in 
          // your object. The data is not hidden. 
    } 
    : 
    : 
} 

当返回引用类型到你的代码的客户,有两种方法,以保持数据隐藏和封装:

  • 返回一个不可变的引用类型。不可变的对象可以只有一个状态。该状态无法更改,因此即使您返回参考,您的数据仍然安全。

  • 返回a 防御性副本。将引用类型的数据复制到一个新对象中,并将其返回给您的代码的用户。即使用户修改了返回的对象,你自己的对象仍然保持完好。

为了使上面的例子安全返回到您的API的客户,我们可以简单地创建使用clone()法阵的副本:

public class MyClass2 { 
    private int[] myField2; 

    public MyClass2(int[] initVal) { this.myField = initVal; } 

    public int[] getValue() { 
     return myField2.clone(); // now safe because we return a defensive 
            // copy of our field. Changes to the 
            // returned object won't affect this object. 
    } 
    : 
    : 
} 

防守复印的最佳实践之一用来保护对象状态的完整性。