如果返回值是可变的,为什么 getter & setter?

2022-09-02 12:33:23

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

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

如果是这样的话(如果我在这里有严重的误解,请纠正我),为什么不声明数据成员并简化事情呢?public


答案 1

在java中返回值是返回已经的对象类型(如String)或返回不可变对象的副本的问题。immutableimmutable


示例 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 用于方便,因为复杂类型实现了可克隆接口。

此外,为了避免子类覆盖不可变方法,您可以声明它们。作为旁注,生成器模式通常用于构造不可变对象。final


答案 2

如果您希望类是不可变的(即只有字段和 getter),则必须确保返回的值也是不可变的。返回字符串和内置基元时,您可以免费获得此内容,但是对于其他数据类型,还需要执行一些额外的步骤:final

  • 使用不可变的装饰器包装收藏品,或在从采集器返回之前防御性地复制它们
  • 复制和DateCalendar
  • 仅返回不可变对象或防御性对象。这也适用于集合中的对象。clone

请注意,如果以防御性方式复制集合,则客户端可以查看或修改副本,但这不会影响原始集合:

return new ArrayList<Foo>(foos);

另一方面,如果包装原始集合,则客户端能够看到在创建包装器后引入集合的所有更改,但尝试更改包装器的内容将导致运行时异常:

return Collections.unmodifiableList(foos);

底线是:也必须是不可变的,否则集合是不可变的,但客户端代码仍然可以修改集合的成员。因此,相同的规则适用于 。FooFoo

如果是这样的话(如果我在这里有严重的误解,请纠正我),为什么不宣布数据成员是公开的,并简化事情呢?

因为:

  • 您可能希望将可变数据存储在对象内,并且仅提供数据的不可变(只读)视图(如包装集合)
  • 您可以在将来更改实现,删除字段,例如动态计算值。

推荐