【问题标题】:Why is sharing the mutable internals of immutable objects a good idea?为什么共享不可变对象的可变内部是个好主意?
【发布时间】:2019-02-10 20:37:38
【问题描述】:

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

您不仅可以共享不可变对象,还可以共享其内部。

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

确保对可变组件的独占访问。

作为共享可变内部结构可能存在问题的一个示例,在我看来,如果同一类的两个实例 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}

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

【问题讨论】:

  • 在我看来,共享不可变对象的可变内部并不是一个好主意。唯一的例外是,如果你能以某种方式保证两个实例都不会改变它的任何可变组件(这可能是 Josh 的意思)。这里的问题是,在 Java 中,您不能对数组之类的东西强制执行不变性。
  • BigInteger 是不可变的。所以它的内部幅度数组永远不会改变。因此,与另一个保证永远不会改变其大小的不可变 Integer 共享它不是问题。 BigInteger 不与未知物体共享其大小。它与 BigInteger 的其他实例共享它,从而保证量级的不变性。
  • JB 说的是对的。尽管内部数组是“可变的”,但它实际上从未改变。这意味着它确实是不可变的(结合 Java 中关于同步和发生前发生的其他一些规则)。因此,即使“可变内部”看起来是可变的,但实际上并非如此。这就是为什么可以分享的原因。

标签: java immutability effective-java


【解决方案1】:

如果您以不可变的方式公开对象的内部,则可以共享不可变对象的内部。

例如:

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);
  }
}

数组是一个低效的例子,但它仍然是可能的。更高效的做法是将内部状态存储/公开为某种Collection&lt;String&gt;,这样您就可以将内部状态设置为不可修改的对象:

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));
  }

}

【讨论】:

  • 请注意,OP 正在谈论与同一类的其他对象共享内部状态(“可变”数组)。这与将内部结构公开给任何类并不完全一样,就像您在此处所做的那样。
  • 是的,我主要专注于回答第一个问题。第二个问题由 cmets 中的 JB Nizet 回答
猜你喜欢
  • 2017-10-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-18
  • 2012-11-30
  • 2012-02-08
  • 2010-09-21
相关资源
最近更新 更多