当存在可变 class 的引用时,如何将不可变性设置为 class
How to set Immutablity to the class when reference of mutable class is present
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;
}
}
我想为 class IMClass 提供完全不变性,我们可以看到 IMclass 是不可变的,但它有一个实例变量 mclass 是 MClass 的引用MClass 是一个可变的 class。
我试过如下更改 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);
}
}
}
问题是 MClass
的 clone()
方法在 ImClass
中不可见。
当您将以下方法添加到 MClass
时它将起作用:
@Override
public Object clone() {
try {
return super.clone();
} catch (Exception e) {
return null;
}
}
并更改您的构造函数以将对象也克隆到那里(正如 Jon Skeet 的评论):
public ImClass(MClass mClass) {
this.mClass = (MClass)mClass.clone();
}
如果您正在尝试围绕可变 class 实现某种不可变包装器,也许更好的想法是扩展它并覆盖它发生变异的所有地方。
class IMWrapper extends MClass {
public IMWrapper(int id) {
super.setId(id);
}
@Override
void setId(int id) {
throw new UnsupportedOperationException("you can't modify this instance");
}
...
}
防御性复制是个好主意,您应该只为 MClass
:
实现复制构造函数
class MClass {
// ...
public MClass(MClass copied) {
this.id = copied.id;
}
}
您已经将问题缩小到 copying/cloning 个对象。
您可以在这里找到解决方案:How do I copy an object in Java?
周围有很多好主意。以下是我的总结:
- 尽可能避免使用
clone
,而倾向于使用复制构造函数。参见 Joshua Bloch's thoughts on this matter。
- 为确保不变性,您需要确保复制传递给
ImClass
构造函数的 MClass
实例。否则,最初通过 MClass
实例的人仍然可以对其进行更改。
- 考虑围绕
MClass
class 创建一个不可变的包装器,或许可以使用继承。
这是实现这一目标的一种方式。当然还有其他方法:
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
方法起作用
如果您仍想以克隆方式进行,请确保遵循以下 2 个步骤:
- 将
clone
公开为 public 方法(正如已经建议的那样),但理想情况下,不要吞下异常,这样你就不会得到莫名其妙的 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 {
// ...
}
但同样,要确保 ImClass
中的私有 MClass
实例是 "immutable",您需要在 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
}
我的工作代码
public class ImmutabilityOfReferenceInstance {
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
}
}
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);
}
}
}
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;
}
}
我想为 class IMClass 提供完全不变性,我们可以看到 IMclass 是不可变的,但它有一个实例变量 mclass 是 MClass 的引用MClass 是一个可变的 class。 我试过如下更改 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);
}
}
}
问题是 MClass
的 clone()
方法在 ImClass
中不可见。
当您将以下方法添加到 MClass
时它将起作用:
@Override
public Object clone() {
try {
return super.clone();
} catch (Exception e) {
return null;
}
}
并更改您的构造函数以将对象也克隆到那里(正如 Jon Skeet 的评论):
public ImClass(MClass mClass) {
this.mClass = (MClass)mClass.clone();
}
如果您正在尝试围绕可变 class 实现某种不可变包装器,也许更好的想法是扩展它并覆盖它发生变异的所有地方。
class IMWrapper extends MClass {
public IMWrapper(int id) {
super.setId(id);
}
@Override
void setId(int id) {
throw new UnsupportedOperationException("you can't modify this instance");
}
...
}
防御性复制是个好主意,您应该只为 MClass
:
class MClass {
// ...
public MClass(MClass copied) {
this.id = copied.id;
}
}
您已经将问题缩小到 copying/cloning 个对象。
您可以在这里找到解决方案:How do I copy an object in Java?
周围有很多好主意。以下是我的总结:
- 尽可能避免使用
clone
,而倾向于使用复制构造函数。参见 Joshua Bloch's thoughts on this matter。 - 为确保不变性,您需要确保复制传递给
ImClass
构造函数的MClass
实例。否则,最初通过MClass
实例的人仍然可以对其进行更改。 - 考虑围绕
MClass
class 创建一个不可变的包装器,或许可以使用继承。
这是实现这一目标的一种方式。当然还有其他方法:
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
方法起作用
如果您仍想以克隆方式进行,请确保遵循以下 2 个步骤:
- 将
clone
公开为 public 方法(正如已经建议的那样),但理想情况下,不要吞下异常,这样你就不会得到莫名其妙的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 {
// ...
}
但同样,要确保 ImClass
中的私有 MClass
实例是 "immutable",您需要在 2 个地方调用 clone
:
- 在
ImClass.getmClass()
方法中,就像您已经在做的那样。 - 也在
ImClass
构造函数中。如果你忘记了这个,那么它仍然可以修改它,所以还没有完全实现不变性。
像这样:
public ImClass(MClass mClass) {
this.mClass = (MClass)mClass.clone();
}
编辑 2:关于为什么您的代码似乎仍然无法正常工作
代码现在应该可以工作了,但是如果我查看您当前的 main
方法,您没有正确测试不变性。您正在检查 ImClass
.
以下是更有效的测试:
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
}
我的工作代码
public class ImmutabilityOfReferenceInstance {
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
}
}
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);
}
}
}