为什么不可变 class 中的可变对象可以访问?

Why is the mutable object within the immutable class accessible?

这是一个不可变的例子 class:

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 class:

的实现
package com.immutable;

public class ComplexObj {

    int  someVar;

    public ComplexObj(int i){
        this.someVar = i;
    }
}

当我创建 ImmutableClass 的实例时,我在 ImmutableClass 的构造函数中制作了 ComplexObj 的深拷贝,但是我能够更新 [=17] 的值=] 通过 icls.cObj.someVar 打破了我的 class 的不变性。我在这里做错了什么?

你的不可变 class 就像一座钛和混凝土的纪念碑。一旦创建,它几乎不会受到破坏。

你的纪念碑上写着一个非常漂亮的沙堡在海滩上的位置。

一个人希望他们充分享受您的纪念碑,包括找到它、开车去那里以及凝视沙堡。

第二个人开车过去,把城堡夷为平地。

现在第一个人觉得他们的体验被改变了。

第三个人决定就什么是不可变的进行哲学辩论,并说纪念碑根本没有改变:沙堡的那个位置仍然在那里,没有改变。

第一人称和第三人称为此大打出手

你告诉我,谁是对的?第一还是第三?

因为它与您的 java 代码中发生的事情完全匹配。你就像第一个人。谁说'make a class final, and every field final, and then the objects of that class will be immutable'像老三

如果你想让纪念碑的经验不变,那么要么那个沙堡也需要是一个titanium-and-concrete概念,这不是这个建筑的建造者的东西纪念碑可以做(你必须要求沙堡的建造者这样做),或者你不需要把 non-impervious 东西的位置放在那个纪念碑上。

换句话说,如果您希望 'experience' 不可变,则不要在 class 中包含 non-immutable 类型的字段 - 即没有类型的字段ComplexObj,或者,也使它们不可变,即编辑 ComplexObj.java,例如使该字段 final.

'immutable' 仅适用于与相关 class 关联的字段。它说 class 中定义的字段不能更改。

在这种情况下,ImmutableClass 实例中的一个字段是对另一个对象(ComplexObj 实例)的引用。 ImmutableClass 的所有不变性表示该字段在创建对象后无法更改。换句话说,您不能将 ImmutableClass 的实例更改为指向与它最初指向的对象不同的 ComplexObj 对象。

然而,其中的

None 与更改 ImmutableClass 实例中的字段指向的 ComplexObj 有任何关系。如果对 class 的引用可供外部调用者使用,则调用者可以获取该引用,并修改该对象(如果它通常允许修改自身)。 ImmutableClass 不可变与它的字段指向的对象是否可以更改无关。

你的 class 没问题,只要 cObj 后面的可变对象的引用不脱离 ImmutableClass 的包装实例的范围,它就是真正不可变的。

您可以访问 cObj 并通过 icls.cObj.someVar 更改它的一个字段,因为您在 main 中执行此操作,这是 class 的方法13=]。 Class class C 的方法(标有 static 的方法)能够访问 C 的私有 class 和对象字段。

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