public class ImmutabilityOfReferenceInstance {
public static void main(String[] args) {
MClass mc = new MClass();
mc.setId(1);
ImClass imc1 = new ImClass(mc);
System.out.println("imc1 = "+imc1);
mc.setId(2);
ImClass imc2 = new ImClass(mc);
System.out.println("imc2 = "+imc2);
}
}
final class ImClass {
final private MClass mClass;
public ImClass(MClass mClass) {
this.mClass = mClass;
}
public MClass getmClass() {
return mClass;
}
@Override
public String toString() {
return String.valueOf(mClass.getId());
}
}
class MClass {
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
我想为IMClass类提供完全的不变性,正如我们所看到的,IMclass是不变的,但是它具有实例变量mclass,它是MClass的引用,而MClass是可变的类。
我尝试过如下更改getter方法getmClass()
public MClass getmClass() {
return (MClass) mClass.clone();
}
但它不允许我这样做,有人可以纠正一下我错了吗。
提前致谢
I have tried this but still getting the same result, values are getting updated
public class ImmutabilityOfReferenceInstance {
public static void main(String[] args) {
MClass mc = new MClass();
mc.setId(1);
ImClass imc1 = new ImClass(mc);
System.out.println("imc1 = "+imc1);
mc.setId(2);
ImClass imc2 = new ImClass(mc);
System.out.println("imc2 = "+imc2);
}
}
final class ImClass {
final private MClass mClass;
public ImClass(MClass mClass) {
this.mClass = (MClass)mClass.clone();
}
public MClass getmClass() {
return (MClass)mClass.clone();
}
@Override
public String toString() {
return String.valueOf(mClass.getId());
}
}
class MClass implements Cloneable{
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
最佳答案
有很多好主意。这是我的总结方法:
如果可能,请避免使用clone
,而建议使用复制构造函数。请参见Joshua Bloch's thoughts on this matter。
为了确保不变性,您需要确保复制传递给MClass
构造函数的ImClass
实例。否则,最初通过MClass
实例的人仍然可以对其进行更改。
考虑围绕MClass
类创建一个不变的包装器,也许可以使用继承。
这是可以实现的一种方式。当然还有其他方法:
public class ImmutabilityOfReferenceInstance {
public static void main(String[] args) {
MClass mc = new MClass();
mc.setId(1);
ImClass imc1 = new ImClass(mc);
System.out.println("imc1 before = " + imc1);
mc.setId(2);
System.out.println("imc1 after = " + imc1); // continues printing 1.
imc1.getmClass().setId(3); // changes not allowed on the immutable copy, throws exception.
}
}
public final class ImClass {
final private MClass mClass;
public ImClass(MClass mClass) {
this.mClass = (mClass == null ? null : mClass.createImmutableCopy());
}
public MClass getmClass() {
return mClass;
}
@Override
public String toString() {
return String.valueOf(mClass.getId());
}
}
public class MClass {
private int id;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public MClass createImmutableCopy() {
return new ImmutableMClass(this);
}
private static class ImmutableMClass extends MClass {
public ImmutableMClass(MClass src) {
super.setId(src.getId());
}
@Override
public void setId(int id) {
throw new UnsupportedOperationException("immutable instance.");
}
}
}
编辑:如何使
clone
方法工作如果您仍然想要克隆,请确保遵循以下两个步骤:
将
clone
公开为公共方法(如已建议的那样),但是,理想情况下,不要吞下异常,以便在某些不起作用的情况下不会得到无法解释的NullPointerException
。尽管从技术上讲,如果您不忘记步骤2,就永远不会发生CloneNotSupportedException
异常。像这样:
@Override
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
确保
MClass
实现了Cloneable
接口。像这样:
public class MClass implements Cloneable {
// ...
}
但是再次,要确保
MClass
中的私有ImClass
实例是“不可变的”,您需要在2个地方调用clone
:在
ImClass.getmClass()
方法中,就像您已经做的那样。同样在
ImClass
构造函数中。如果您忘记了它,那么仍然可以对其进行修改,因此尚未完全实现不变性。像这样:
public ImClass(MClass mClass) {
this.mClass = (MClass)mClass.clone();
}
编辑2:关于为什么您的代码仍然无法正常工作的原因
该代码现在应该可以正常工作,但是如果我看一下您当前的
main
方法,您就不能正确测试不变性。您正在检查来自ImClass
的2个不同实例的值。以下是更有效的测试:
public static void main(String[] args) {
MClass mc = new MClass();
mc.setId(1);
ImClass imc = new ImClass(mc);
System.out.println("imc = " + imc); // should print 1
mc.setId(2);
System.out.println("imc = " + imc); // should still print 1 if immutability works
imc.getmClass().setId(3);
System.out.println("imc = " + imc); // should still print 1 if immutability works
}