下面是一个不可变类的示例:
package com.immutable;
public final class ImmutableClass {
private final int index;
private final String tStr;
private final ComplexObj cObj;
public ImmutableClass(int i, String s, ComplexObj o){
this.index = i;
this.tStr = s;
ComplexObj cobj = new ComplexObj(o.someVar);
this.cObj = cobj;
}
public static void main(String[] args) {
ImmutableClass icls = new ImmutableClass(5,"Hello World",new ComplexObj(100));
System.out.println(icls.index + " | " + icls.tStr + " | " + icls.cObj.someVar);
icls.cObj.someVar = 5;
System.out.println("Second run :" + icls.index + " | " + icls.tStr + " | " + icls.cObj.someVar);
}
}
下面是ComplexObj
类的实现:
package com.immutable;
public class ComplexObj {
int someVar;
public ComplexObj(int i){
this.someVar = i;
}
}
当我创建ImmutableClass
的实例时,我正在ImmutableClass
的构造函数中制作ComplexObj
的深层副本,但是我能够通过icls.cObj.someVar
更新cObj
的值,这种破坏了我的类的不变性。我在这里做错了什么?
你的不可变类就像一个钛和混凝土纪念碑。一旦创建,它几乎不受破坏的影响。
写在你的纪念碑上是一个非常漂亮的沙堡海滩上的位置。
一个人希望他们充分享受你的纪念碑,包括找到它,开车到那里,凝视沙堡。
第二个人开车到那边,把城堡夷为平地。
第一个人现在觉得他们的体验被改变了。
第三个人决定就不变的含义进行哲学辩论,并说纪念碑根本没有改变:沙堡的位置仍然存在,没有改变。
第一个和第三个人决定为此打架。
你告诉我,谁是对的?第一个,还是第三个?
因为它与 java 代码中发生的事情完全匹配。你就像第一个人。谁说"让一个班级成为最终的,每个领域最终的,然后那个类的对象将是不变的"就像第三个
。如果你想让纪念碑的体验不改变,那么要么沙堡也需要是一个钛和混凝土的概念,这不是这座纪念碑的建造者可以做的事情(你必须要求沙堡的建造者这样做),或者你不需要把非透水物体的位置放在纪念碑上。
换句话说,如果您希望类中的"经验"不可变,请不要在类中包含不可变类型的字段 - 即没有类型ComplexObj
的字段,或者,也使这些字段不可变,即编辑ComplexObj.java
,例如使该字段final
。
不可变"仅与相关类关联的字段有关。 它说类中定义的字段不能更改。
在这种情况下,ImmutableClass
实例中的一个字段是对另一个对象(ComplexObj
实例)的引用。ImmutableClass
的所有不变性都表示,在创建对象后无法更改该字段。 换句话说,不能将ImmutableClass
实例更改为指向与最初指向的对象不同的ComplexObj
对象。
但是,这些都与更改ImmutableClass
实例中的字段指向的ComplexObj
无关。 如果对该类的引用可供外部调用方使用,则调用方可以获取该引用,并在通常允许修改自身的情况下修改该对象。ImmutableClass
不可变并不能说明其字段指向的对象是否可以更改。
你的类很好,只要cObj
后面的可变对象的引用不逃脱ImmutableClass
包装实例的范围,它就是真正不可变的。
您可以通过icls.cObj.someVar
访问cObj
并更改其字段,因为您可以在main
中执行此操作,这是ImmutableClass
的类方法。类的类方法(标有static
的方法)C
能够访问C
的私有类和对象字段。
将类ImmutableClass
移动到它自己的文件中,你会注意到icls.cObj.someVar
会在编译时给你一个错误。或者以您喜欢的任何其他方式从ImmutableClass
中提取main
。例如:
public class ImmutableClass {
private final int index;
private final String tStr;
private final ComplexObj cObj;
public ImmutableClass(int i, String s, ComplexObj o){
this.index = i;
this.tStr = s;
ComplexObj cobj = new ComplexObj(o.someVar);
this.cObj = cobj;
}
}
class Main {
public static void main(String[] args) {
ImmutableClass icls = new ImmutableClass(5,"Hello World",new ComplexObj(100));
System.out.println(icls.index + " | " + icls.tStr + " | " + icls.cObj.someVar);
icls.cObj.someVar = 5;
System.out.println("Second run :" + icls.index + " | " + icls.tStr + " | " + icls.cObj.someVar);
}
}