为什么要吸气如果返回值是可变的,那么setter?

时间:2011-09-12 13:40:34

标签: java getter-setter getter

在C ++中,一个getter&由于能够通过const返回值控制可变性,因此私有数据成员的setter非常有用。

在Java中,如果我理解正确(如果我弄错了请纠正我),在getter上指定final不会那样。一旦调用者通过getter接收到数据成员引用,它就可以修改它,尽管它是私有的......

如果是这种情况(如果我在这里有严重误解,请纠正我),为什么不宣布数据成员public并简化事情?

5 个答案:

答案 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),您必须确保返回的值也是不可变的。返回字符串和内置基元时可以免费获得,但是其他数据类型需要一些额外的步骤:

  • 使用不可变装饰器包装集合或在从getter返回之前防御性地复制它们
  • 制作DateCalendar
  • 的副本
  • 只返回不可变对象或防御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对于控制对引用对象的修改也很有价值。你可以在二传手中制作防御性副本。