为什么共享不可变对象的可变内部结构是个好主意?

Why is sharing the mutable internals of immutable objects a good idea?

我正在阅读 Joshua Bloch 的 "Effective Java",有两个问题。布洛赫陈述如下。

Not only can you share immutable objects, but they can share their internals.

然后他继续给出一个例子,其中 BigInteger 的一个实例与另一个 BigInteger 实例共享其幅度数组。我的第一个问题是:这是否违反了 Bloch 之前制定的规则,即

Ensure exclusive access to mutable components.

作为共享可变内部结构如何成为问题的示例,在我看来,如果相同 class 的两个实例 sample1sample2 可以共享它们的内部结构,那么您可能有以下内容。


public final class Sample {

    private final int[] field1;
    private final int[] field2;

    public Sample(int[] field1, int[] field2) {
        this.field1 = field2;
        this.field2 = field2;
    }

    public Sample(int[] field1, Sample sampleForField2) {
        this.field1 = field1;
        for (int i = 0; i < sampleForField2.field2.length; i++) {
            sampleForField2.field2[i] += 1;
        }
        this.field2 = sampleForField2.field2;
    }

    public static void main(String[] args) {
        Sample sample1 = new Sample(new int[]{0, 1}, new int[]{10, 11});
        Sample sample2 = new Sample(new int[]{20, 21}, sample1);
        System.out.println(Arrays.toString(sample1.field2));
    }

}

在上面的示例中,a 能够通过构造函数 public Sample(int[] field1, Sample sampleForField2)b 共享其内部结构。这会导致混叠,导致 sample1field2 值为 {11, 12}

因此,我的第二个问题是:在实例之间共享可变内部结构不会破坏不变性吗?

共享不可变对象的内部结构很好,前提是您以不可变的方式公开对象的内部结构。

例如:

public class ImmutableObj {

  private final String[] internals;

  public ImmutableObj(String[] strings) {
    internals = Arrays.copyOf(strings, strings.length);
  }

  public String[] getInternals() {
    // return a copy of the internal state, since arrays
    // do not offer an immutable view
    return Arrays.copyOf(internals, internals.length);
  }
}

数组是一个低效的例子,但它仍然是可能的。一个更高效的事情是 storing/exposing 内部状态作为某种 Collection<String> 所以你可以将内部设置为一个不可修改的对象开始:

public class ImmutableObj {

  // Simply make this public instead of getter since it is read-only
  public final List<String> internals;

  public ImmutableObj(String[] strings) {
    internals = Collections.unmodifiableList(Arrays.asList(strings));
  }

}