Java不可变类规则
下面的类是不可变的:Java不可变类规则,java,immutability,Java,Immutability,下面的类是不可变的: final class MyClass { private final int[] array; public MyClass(int[] array){ this.array = array; } } 不,这不是因为数组的元素仍然可以更改 int[] v1 = new int[10]; MyClass v2 = new MyClass(v1); v1[0] = 42; // mutation visible to MyClass
final class MyClass {
private final int[] array;
public MyClass(int[] array){
this.array = array;
}
}
不,这不是因为数组的元素仍然可以更改
int[] v1 = new int[10];
MyClass v2 = new MyClass(v1);
v1[0] = 42; // mutation visible to MyClass1
没有办法使数组不可变。也就是说,无法阻止任何客户端代码设置、删除或向数组中添加项 这里有一个真正不变的选择:
private static class MyClass
{
private List<Integer> list;
private MyClass(final int[] array)
{
final List<Integer> tmplist = new ArrayList<Integer>(array.length);
for (int i : array)
{
tmplist.add(array[i]);
}
this.list = Collections.unmodifiableList(tmplist);
}
}
私有静态类MyClass
{
私人名单;
私有MyClass(最终int[]数组)
{
最终列表tmplist=newarraylist(array.length);
for(int i:array)
{
tmplist.add(数组[i]);
}
this.list=Collections.unmodifiableList(tmplist);
}
}
我关于不变性规则的两分钱(我在阅读《有效Java——一本好书》时保留了这些规则!):
java.util.Date
实例在形式上是可变的(在它上面有一个setTime()
方法),但是习惯上处理它就像它是不可变的一样;这只是一个应用程序范围的约定,即不应调用Date.setTime()
方法
作为补充说明:
- 不变性通常被认为是“外部特征”。例如,Java的
字符串
被证明是不可变的(Javadoc就是这么说的)。但是,如果您查看源代码,您将看到
实例包含一个名为字符串
的私有字段,该字段可能会随着时间的推移而变化:这是对哈希
返回的值的缓存。我们仍然说哈希代码()
是不可变的,因为String
字段是一个内部优化,从外部看不到任何效果hash
- 通过反射,可以修改最私有的实例字段(包括那些标记为
)的字段,如果程序员愿意的话。这并不是一个好主意:它可能会打破使用上述实例的其他代码片段所使用的假设。正如我所说,不变性是一种惯例:如果程序员想与自己抗争,那么他可以,但这可能会对生产率产生不利的副作用final
- 大多数Java值实际上都是引用。定义引用对象是否是你认为是“实例内容”的一部分取决于你。在类中,有一个字段引用(外部提供的)整数数组。如果以后修改了该数组的内容,你会认为这会破坏你的< MyClass > <代码>实例的不可变性吗?这个问题没有一般性的答案
- 要使类不可变,您需要确保类上的所有字段都是最终字段,并且这些字段的类型也不可变
这可能是一个痛苦的记忆,但有一个工具可以帮助你
提供注释
@ImmutableValue
,您可以将其添加到接口或类中
有一个maven插件要在编译时检查您是否符合以下关于不变性的规则
希望这有帮助。啊,对了!我总是忘记引用是分配给私有成员的。@Chaos:它不可能是包装器。它必须获取传入数组的副本。它不能包装原始调用方仍然可以变异的任何内容。@Jon-一个简单的语义失礼就够了。我的错误是认为一个副本是隐含的。您必须将对该数组的所有引用都保留在内部。您可以允许在不返回整个数组的情况下检索单个元素,也可以使用OO方法编程,方法是将需要访问数组的代码放在这个类本身中,使它既不可变又不可变。那么这是不可变的吗?最终类MyClass{private final int[]数组;public MyClass(int[]数组){this.array=array.clone();}}}否,不可修改列表和asList都创建视图。因此,对数组的外部更改(如JaredPar的回答中所述)将导致此版本的MyClass发生更改。
Arrays.asList(array)
也将返回一个List
。曾经被否决的人不理解不可变契约。如果返回一个可以更改的数据结构,不管它是否是某个备份数组的副本,它仍然不是不可变的。克隆某个东西并返回一个可变克隆并不能使一个类100%不可变,这也违反了最不令人惊讶的原则。asList不创建“视图”,而是将数组的可变副本创建为列表。不可修改的只是,它返回一个不可修改的列表,因此是不可变的。这里的教训是不要使用数组和适当的集合类作为开始。不要实现Serializable
。