英文:
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);
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论