为什么不可变类中的可变对象是可访问的?



下面是一个不可变类的示例:

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

最新更新