不可变类?

2022-08-31 11:22:38

如何使Java类不可变,不可变性的必要性是什么,使用它有什么好处吗?


答案 1

什么是不可变对象?

不可变对象是在实例化后不会更改状态的对象。

如何使对象不可变?

通常,可以通过定义一个没有公开其任何成员并且没有任何 setter 的类来创建不可变对象。

以下类将创建一个不可变对象:

class ImmutableInt {
  private final int value;

  public ImmutableInt(int i) {
    value = i;
  }

  public int getValue() {
    return value;
  }
}

从上面的示例中可以看出,只能在实例化对象时设置 的值,并且通过仅具有 getter () 对象的状态,在实例化后无法更改。ImmutableIntgetValue

但是,必须注意对象引用的所有对象也必须是不可变的,否则可以更改对象的状态。

例如,允许对数组的引用或通过 getter 获取,将允许通过更改数组或集合来更改内部状态:ArrayList

class NotQuiteImmutableList<T> {
  private final List<T> list;

  public NotQuiteImmutableList(List<T> list) {
    // creates a new ArrayList and keeps a reference to it.
    this.list = new ArrayList(list); 
  }

  public List<T> getList() {
    return list;
  }
}

上述代码的问题在于,可以通过获取和操作,导致对象本身的状态被改变,因此,不是不可变的。ArrayListgetList

// notQuiteImmutableList contains "a", "b", "c"
List<String> notQuiteImmutableList= new NotQuiteImmutableList(Arrays.asList("a", "b", "c"));

// now the list contains "a", "b", "c", "d" -- this list is mutable.
notQuiteImmutableList.getList().add("d");

解决此问题的一种方法是在从 getter 调用时返回数组或集合的副本:

public List<T> getList() {
  // return a copy of the list so the internal state cannot be altered
  return new ArrayList(list);
}

不可变性的优势是什么?

不可变性的优势在于并发性。很难保持可变对象的正确性,因为多个线程可能试图更改同一对象的状态,导致某些线程看到同一对象的不同状态,具体取决于对所述对象的读取和写入的时间。

通过拥有不可变对象,可以确保所有查看该对象的线程都将看到相同的状态,因为不可变对象的状态不会改变。


答案 2

除了已经给出的答案之外,我建议阅读《有效Java》第2版中的不可变性,因为有一些细节很容易被遗漏(例如防御性副本)。另外,Effective Java 2nd Ed. 是每个 Java 开发人员的必读书目。