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
}

10-07 15:52