在C ++中,一个getter&由于能够通过const
返回值控制可变性,因此私有数据成员的setter非常有用。
在Java中,如果我理解正确(如果我弄错了请纠正我),在getter上指定final
不会那样。一旦调用者通过getter接收到数据成员引用,它就可以修改它,尽管它是私有的......
如果是这种情况(如果我在这里有严重误解,请纠正我),为什么不宣布数据成员public
并简化事情?
答案 0 :(得分:23)
在java中使immutable
返回值是 返回已经immutable
个对象类型(例如String)或返回副本的问题用于非不可变对象。
示例1 - 已经是不可变对象
public String getValue() {
return value;
}
示例2 - 已经不可变对象的集合
public List<String> getValues() {
return new ArrayList<String>(values);
}
示例3 - 非永久对象
public Complex getComplex() {
return complex.clone();
}
示例4 - 非不可变对象的集合
public List<Complex> getComplex() {
List<Complex> copy = new ArrayList<Complex>(complexs.size());
for (Complex c : complexs)
copy.add(c.clone());
return copy;
}
示例3和4是基于复杂类型实现Cloneable
接口的便利性。
此外,为避免子类重写不可变方法,可以声明它们final
。作为旁注,builder
模式通常用于构造不可变对象。
答案 1 :(得分:5)
如果您希望您的类是不可变的(即只有final
个字段和getter),您必须确保返回的值也是不可变的。返回字符串和内置基元时可以免费获得,但是其他数据类型需要一些额外的步骤:
Date
和Calendar
clone
它们。这也适用于集合中的对象。请注意,如果您防御性地复制集合,客户端可以查看或修改副本,但这不会影响原始集合:
return new ArrayList<Foo>(foos);
另一方面,如果您包装原始集合,客户端能够看到在创建包装器之后引入到集合的所有更改,但尝试更改包装器的内容将导致运行时异常:
return Collections.unmodifiableList(foos);
底线是:Foo
也必须是不可变的,否则集合是不可变的,但客户端代码仍然可以修改集合的成员。因此,相同的规则适用于Foo
。
如果是这样(如果我在这里有严重的误解,请纠正我),为什么不公开数据成员并简化事情呢?
由于:
答案 2 :(得分:3)
如果要返回可变标准容器(例如列表)的不可变视图,那么您应该查看Collections库:
http://download.oracle.com/javase/1.4.2/docs/api/java/util/Collections.html
它提供了一些有用的包装器,例如unmodifiableMap和unmodifiableList。这样你就不必制作一份浪费的副本。当然,如果列表的元素是可变的,那么这将无济于事 - 在Java中没有简单的方法可以获得“深层”不变性。当然,在C ++中也是如此 - 例如,如果你有一个指向Foo对象的指针的const向量,那么Foo对象本身仍然可以修改(因为const不会跨指针传播)。
答案 3 :(得分:0)
如果是这种情况(如果我在这里有严重的误解,请纠正我),为什么不公开数据成员并简化事情呢?
首先,JavaBeans规范。要求你提供getter(以及可变属性的setter)。
其次,getter可能会让你添加一些逻辑,例如一个getter实际上可能决定返回什么(例如,如果属性为null,则返回一些不同的东西)。如果您没有首先使用getter,那么以后添加此类逻辑会有更多麻烦。使用getter,您只需更改方法而无需触及调用者。
答案 4 :(得分:0)
为什么不公开数据成员并简化事情?
因为信息隐藏使得管理和维护复杂的代码库变得更加容易。如果数据成员是私有的,则可以在一个类中更改表示和行为,而不是在整个大型代码库中。
一旦调用者通过getter接收到数据成员引用,它就可以修改它,尽管它是私有的......
为了澄清,调用者无法修改从getter返回的数据成员。它可能能够修改数据成员指向的对象。
如果这是一个问题,并且您通过getter提供访问权限,则可以返回不可变实例或防御副本。
setter对于控制对引用对象的修改也很有价值。你可以在二传手中制作防御性副本。