为什么在不可变类内部可以访问可变对象?

huangapple go评论126阅读模式
英文:

Why is the mutable object within the immutable class accessible?

问题

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

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 的值,这在一定程度上破坏了我的类的不可变性。我在这里做错了什么?

英文:

Here is an example of an immutable 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);

    }
}

And here is the implementation of the ComplexObj class:

package com.immutable;

public class ComplexObj {

    int  someVar;

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

When I create an instance of ImmutableClass I am making a deep copy of ComplexObj in the constructor of ImmutableClass, however I was able to update the value of cObj via icls.cObj.someVar that kind of breaks immutability of the my class. What am I doing wrong here?

答案1

得分: 1

你的不可变类就像是一座钛和混凝土的纪念碑。一旦创建,它基本上不容易遭受破坏。

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

一个人期待完全欣赏你的纪念碑,包括找到那个地方,开车过去,凝视着沙堡。

第二个人开车过去,把沙堡压扁了。

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

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

第一个和第三个人决定进行一场拳击比赛。

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

因为这与你的Java代码中发生的情况完全匹配。你就像第一个人。无论谁说过'让一个类变为final,每个字段都是final,那么该类的对象将是不可变的',就像第三个人一样。

如果你希望纪念碑的“体验”不改变,那么要么沙堡也需要成为钛和混凝土的概念,这不是这座纪念碑的建造者可以做到的(你需要询问沙堡的建造者来做这个),要么你不应该在纪念碑上放置那些不是坚固的事物的位置。

换句话说,如果你希望它是“体验”不变的,请不要在类中包含非不可变类型的字段 - 也就是说,不要在类中使用ComplexObj类型的字段,或者另一种选择是,使它们也变成不可变的,例如,编辑ComplexObj.java,将那个字段标记为final

英文:

Your immutable class is like a titanium and concrete monument. Once created it is pretty much inpervious to vandalism.

Written on your monument is the location on the beach of a very pretty sand castle.

One person expects their full enjoyment of your monument to include finding that, driving over there, and gazing at the sand castle.

A second person drives over there and flattens the castle out.

The first person now feels their experience was changed.

A third person decides to hold a philosophical debate on what immutable means, and says that the monument has not changed at all: That location of the sand castle is still there, unchanged.

The first and third person decide to have a fist fight about it.

You tell me, who is right? The first, or the third?

Because it is an exact match to what's happening in your java code. You're like the first guy. Whomever said that 'make a class final, and every field final, and then the objects of that class will be immutable' is like the third.

If you want the monument's experience to not change, then either that sand castle needs to also be a titanium-and-concrete concept, which is not something the builders of this monument can do (you'd have to ask the builders of the sand castle to do this), or you need to not put the locations of non-impervious things on that monument.

In other words, either don't include fields of non-immutable types in your class if you want it to be 'experience' immutable - i.e. don't have fields of type ComplexObj, or alternatively, make those immutable too, i.e. edit ComplexObj.java and e.g. make that field final.

答案2

得分: 0

'immutable' 只与所讨论的类相关联的字段有关。它表示类中定义的字段不能更改。

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

然而,所有这些都与更改 ImmutableClass 实例内部字段所指向的 ComplexObj 无关。如果将对该类的引用提供给外部调用者,那么调用者可以获取该引用并修改该对象,如果该对象通常允许被修改的话。ImmutableClass 的不可变性不涉及其字段所指向的对象是否可以更改。

英文:

'immutable' only pertains to the fields associated with the class in question. It says that the fields defined in the class can't change.

In this case, one of the fields in an ImmutableClass instance is a reference to another object, a ComplexObj instance. All immutability of ImmutableClass says is that that field can't be changed after the object is created. In other words, you can't change an instance of ImmutableClass to point to a different ComplexObj object than the one it was initially pointing to.

None of this, however, has anything to do with changing the ComplexObj that the field within the ImmutableClass instance was pointing to. If the reference to that class is made available to an outside caller, then the caller can take that reference, and modify that object if it normally allows itself to be modified. ImmutableClass being immutable says nothing about if objects its fields are pointing to can change.

答案3

得分: 0

你的类是没问题的,并且只要可变对象 cObj 的引用不逃离 ImmutableClass 封装实例的作用域,它就是真正的不可变的。

你可以通过 icls.cObj.someVar 访问 cObj 并且修改它的字段,因为你是在 main 方法内部进行的,而 main 方法是 ImmutableClass 的一个类方法。类方法(用 static 标记的方法)可以访问类 C 的私有类和对象字段。

将类 ImmutableClass 移动到它自己的文件中,你会注意到在编译时 icls.cObj.someVar 会报错。或者以任何你喜欢的方式将 main 方法从 ImmutableClass 中提取出来。例如:

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);
    }
}
英文:

Your class is fine and it's truly immutable as long the reference of the mutable object behind cObj doesn't escape the scope of a wrapping instance of ImmutableClass.

You are able to access cObj and change a field of it via icls.cObj.someVar because you do it within main, which is a class method of ImmutableClass. Class methods (methods marked with static) of a class C are able to access C's private class and object fields.

Move the class ImmutableClass in its own file and you'll notice that icls.cObj.someVar will give you an error at compile time. Or extract main out of ImmutableClass any other way you like. E.g.:

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

huangapple
  • 本文由 发表于 2020年10月27日 06:36:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/64545928.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定