如果对象的非原语包含的字段作为引用该字段的对象的对象句柄传递,那么如果最初传递的字段被更新/更改,那么它是否容易在事后被更改?
public class MutableDog
{
public String name;
public String color;
public MutableDog(String name, String color)
{
this.name = name;
this.color = color;
}
}
public class ImmutableDog // are fields of these objects truly safe from changing?
{
private final String name;
private final String color;
public ImmutableDog(MutableDog doggy)
{
this.name = doggy.name;
this.color = doggy.color;
}
public String getColor()
{
return this.color;
}
}
public static void main(String[] args)
{
MutableDog aMutableDog = new MutableDog("Courage", "Pink");
ImmutableDog anImmutableDog = new ImmutableDog(aMutableDog);
aMutableDog.color = "Pink/Black";
anImmutableDog.getColor().equals(aMutableDog.color); // true or false?
}
本质上,是 ImmutableDog
真正不变?在本例中,使用了字符串。使用可变对象,例如 Collection
,有什么不同吗?
这个问题是对这个答案的回应。
3条答案
按热度按时间juzqafwq1#
ImmutableDog
是真正不可变的,即使它可以从可变对象接收字符串。这是因为String
是不变的。这也展示了不变性的一大好处——你可以传递不可变的对象,而不用担心它们会突然改变。你可能在想
ImmutableDog
可以通过设置MutableDog
示例:然而,
"Pink/Black"
与指定给的字符串示例不同ImmutableDog
这里,所以ImmutableDog
不会改变的。另一方面,如果
ImmutableDog
如果有一个可变类型的字段,那么它就不再是真正不变的了。例如,这里有相同的代码,但是
StringBuilder
:现在不变的狗的颜色将出现变化。您仍然可以通过在构造函数中复制字符串生成器来防范这种情况:
但是,这仍然允许您(意外地)修改
ImmutableDog
班级。所以不要将可变类存储在不可变类中。:)
vsaztqbk2#
它非常简单:java中的任何引用类型最终都指向某个对象。
如果该对象具有可变状态,那么对它的任何引用都可以用来更改该状态。
因此,你是对的:只是把
private final
在此之前,每个字段声明不一定使该类本身不可变。在您的示例中,string类是不可变的(除了使用
Unsafe
). 之后name
以及color
被指定时,引用不能更改,它们指向的对象也不能更改。当然,如果类型是
List
例如,底层对象很可能在其他地方更改。如果您想防止这种情况发生,您必须创建一个传入列表的副本,例如,并保留对该列表的引用。qlckcl4x3#
它真的在所有感官和情境中都是不变的吗?不,它在一定程度上是不变的吗?对。
只要您只执行变量赋值,不变的dog实际上总是保持不变的。这通常是由于传递值语义造成的,本文对此进行了详细解释。
当您复制
color
在ImmutableDog
狗,你基本上是在模仿把手。然后通过指定Pink/Black
值时,可变狗的句柄会更改,但不可变狗仍保留原始句柄,指向原始颜色。与
String
类型,这更进一步,因为字符串是不可变的。因此,对字符串调用任何方法都保证不会修改原始值。所以就字符串而言,是的,不可变的狗是真正不可变的,是可以信任的。收藏确实会有所不同。如果我们稍微改变一下你的课程设计:
并执行以下代码:
打印出以下内容:
这是由于
ImmutableDog
的构造函数正在将句柄复制到acceptedMeals
集合,但新句柄指向与原始集合相同的位置。所以当你通过易变的狗调用修改时ImmutableDog
指向内存中相同的位置,其值也可能被修改。在这种特定情况下,您可以通过执行集合的深度复制来规避此副作用,而不是简单地复制引用句柄:
通过这样做,同样
main
上述方法仅打印以下内容: