java - 当存在可变类的引用时如何将不可变性设置为类

标签 java oop design-patterns immutability

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 .
  • 为确保不变性,您需要确保复制传递给ImClass 构造函数的MClass 实例。否则,最初传递 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方法工作

如果您仍想以克隆方式进行,请确保遵循以下两个步骤:

  1. clone 公开为公共(public)方法(如前所述),但理想情况下,不要吞下异常,这样您就不会在某些情况下得到莫名其妙的 NullPointerException不起作用。尽管从技术上讲,如果您没有忘记步骤 #2,则永远不会发生 CloneNotSupportedException 异常。

像这样:

@Override
public Object clone() {
    try {
        return super.clone();
    } catch (CloneNotSupportedException e) {
        throw new RuntimeException(e);
    }
}
  1. 确保 MClass 实现了 Cloneable 接口(interface)。

像这样:

public class MClass implements Cloneable {
    // ...
}

但同样,要确保 ImClass 中的私有(private) MClass 实例是“不可变的”,您需要调用 clone在 2 个地方:

  • 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
}

关于java - 当存在可变类的引用时如何将不可变性设置为类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31723396/

相关文章:

php - 界面类型提示

c++ - 对不适合作为新类提取的私有(private)方法进行单元测试的好方法是什么?

c++ - 消除积木方案

java - 找不到Tomcat部署资源

java - Android - 无法获取纬度/经度值来存储和传递变量

oop - 省略 super() 和/或 *weakening* 前提条件是否违反里氏替换原则?

c++ - 不允许在同一个类中直接访问成员变量

java - 类未返回预期的输出

java - 将gradle classpath依赖关系更新为3.1.1导致找不到符号类 list 错误

java - 热插拔装修?