番石榴的ImmutableXXX真的不变吗?

我一直在使用番石榴已经有一段时间并且真正信任它,直到我昨天偶然发现了一个例子,这让我想到了。 长话短说,这里是:

public static void testGuavaImmutability(){ StringBuilder stringBuilder = new StringBuilder("partOne"); ImmutableList myList = ImmutableList.of(stringBuilder); System.out.println(myList.get(0)); stringBuilder.append("appended"); System.out.println(myList.get(0)); } 

运行此命令后,您可以看到ImmutableList中的条目值已更改。 如果这里涉及两个线程,则可能碰巧看不到另一个线程的更新。

另外让我对答案非常不耐烦的是有效Java中的Item15,第五点说:

在构造函数中创建defensives副本 – 这似乎很合乎逻辑。

看一下ImmutableList的源代码,我看到了这个:

  SingletonImmutableList(E element) { this.element = checkNotNull(element); } 

所以,实际上没有复制,虽然我不知道在这种情况下如何实现通用深层复制(可能是序列化?)。

那么..为什么他们称之为Immutable呢?

你在这里得到的是不可变深不可变之间的区别

不可变对象永远不会改变,但它引用的任何东西都可能改变。 深层不变性要强得多:基础对象和任何可以从中导航的对象都不会改变。

每种都适合自己的情况。 当您创建自己的类具有Date类型的字段时,该日期由您的对象拥有 ; 它确实是它的一部分。 因此,你应该制作它的防御性副本(在进出的路上!)以提供深刻的不变性。

但是一个集合并没有真正“拥有”它的元素。 他们的州不被视为collections国家的一部分; 它是一种不同类型的容器容器 。 (此外,正如您所说,它对使用的元素类型没有深入了解,因此无论如何都不知道如何复制元素。)

另一个答案指出,番石榴collections品应该使用不可修改的术语。 但是在集合的上下文中,术语“ 不可修改”和“ 不可变”之间存在非常明确的区别,它与浅层与深层不变性无关。 “不可修改”说不能通过你的参考来改变这个实例; “不可变”意味着这个实例不能改变,期限,无论是你还是其他任何演员。

列表本身是不可变的,因为您无法添加/删除元素。 关于不变性,这些要素本身就是独一无二的。 更准确地说,我们有来自历史Java 1.4.2文档的定义:

  • 不支持任何修改操作的集合(例如添加,删除和清除)称为不可修改。 不可修改的集合称为可修改的集合。
  • 另外保证Collection对象中的任何更改都不可见的集合称为不可变。 不可变的集合称为可变集合。

请注意,为了使这些定义有意义,我们必须假设抽象意义上的集合表示该集合的对象之间存在隐含的干扰。 这很重要,因为表示不可变集合的对象本身不是该术语的任何标准定义所不可变的。 例如,它的equals关系没有时间一致性,这是对不可变对象的一个​​至关重要的要求。

就防御性复制而言,请注意这是一个定义不明确的问题,并且Java中永远不会有一般的不可变集合会设法防御性地复制其元素。 另外请注意,这样的集合没有真正存在的不可变集合那么有用 :当你将一个对象放入一个集合时,在99.99%的情况下你希望那个对象在那里,而不是一些其他对象甚至不等于它。

对象不变性 (与集合不变性相对)有一个非常标准的定义,它假定从不可变对象可到达的整个对象图的传递不变性。 但是,从字面上看,这样的定义几乎永远不会在现实世界中得到满足。 两个案例:

  • 面对反思, 没有什么是不可改变的。 即使是final字段也是可写的。
  • 甚至String ,即不可变性的基础, 已被certificate在Java沙箱之外是可变的(没有SecurityManager – 它覆盖了99%的真实Java程序)。

您混合了列表的不变性和它包含的对象的不变性。 在不可变集合中,您不能添加/删除对象,但如果它包含的对象是可变的,您可以在get()之后修改它们。