番石榴的ImmutableXXX真的是不变的吗?

Eug*_*ene 8 java collections immutability guava

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

 public static void testGuavaImmutability(){
     StringBuilder stringBuilder = new StringBuilder("partOne");
     ImmutableList<StringBuilder> myList = ImmutableList.of(stringBuilder);
     System.out.println(myList.get(0));
     stringBuilder.append("appended");
     System.out.println(myList.get(0));
 }
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

 SingletonImmutableList(E element) {
     this.element = checkNotNull(element);
 }
Run Code Online (Sandbox Code Playgroud)

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

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

Kev*_*ion 28

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

不可变对象永远不会改变,但它引用的任何东西都可能改变.深不变性是更强的:既不是基本对象,也可以导航到任何对象它会改变.

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

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

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


Mar*_*nik 12

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

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

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

就防御性复制而言,请注意这是一个定义不明确的问题,并且Java中永远不会有一般的不可变集合能够防御性地复制其元素.注意此外,这样的集合是那么有用比不变的集合,真正存在:当你将物体放入一个集合,在99.99%的情况下,你要的是很反对在那里,而不是一些其他的对象,甚至不等于到它.

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

  • 面对反思,没有什么是不可改变的.甚至final字段都是可写的.
  • 甚至String,不可变性的bastillon 已被证明在Java沙箱之外是可变的(没有SecurityManager覆盖99%的真实Java程序).

  • @Marko Topolnik:Guava的`ImmutableList`肯定是"更不可变的",然后是JDK的不可修改的包装器.它实际上是尽可能不变的 - 没有多少防御性复制和任何可以确保Java中"真正的不变性"的东西.请注意,Java中没有通用的复制方法. (2认同)
  • @Marko Topolnik:同意,这不是一成不变的,但它不可能.即使所有成员都是final和immutable,你仍然可以在globals中存储可变状态或使用`System.setProperty`或其他任何东西.你需要一种不同的语言才能获得真正的不变性. (2认同)