当存在可变类的引用时,如何将Immutablity设置为类

时间:2015-07-30 12:08:18

标签: 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);
        }
    }
}

6 个答案:

答案 0 :(得分:1)

有许多好主意在四处流传。以下是我总结它的方法:

  • 尽可能避免使用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方法正常工作

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

  1. clone作为一种公开方法公开(如已建议的那样),但理想情况下,不要吞下例外情况,这样即使某些事情没有发生,也不会让您感到莫名其妙NullPointerException。工作。虽然从技术上讲,如果你不忘记第2步,就不会发生CloneNotSupportedException例外情况。
  2. 像这样:

    @Override
    public Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
    
    1. 确保MClass实现Cloneable界面。
    2. 像这样:

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

      但同样,为了确保MClass中的私有ImClass实例是"不可变的",您需要在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
      }
      

答案 1 :(得分:0)

问题是clone()中的MClass ImClass方法在MClass中不可见。 将以下方法添加到 @Override public Object clone() { try { return super.clone(); } catch (Exception e) { return null; } } 时,它将起作用:

public ImClass(MClass mClass) {
    this.mClass = (MClass)mClass.clone();
}

并改变你的构造函数以克隆那里的对象(如Jon Skeet的评论):

SessionsHelper

答案 2 :(得分:0)

如果你试图围绕一个可变类实现某种不可变的包装,也许更好的想法是扩展它并覆盖它被变异的所有地方。

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");
  }
  ...
}

答案 3 :(得分:0)

防御性复制是一个好主意,您应该只为MClass实现复制构造函数:

class MClass {
    // ...

    public MClass(MClass copied) {
        this.id = copied.id;
    }
}

答案 4 :(得分:0)

您已经将问题缩小到复制/克隆对象。

您可以在此处找到解决方案:How do I copy an object in Java?

答案 5 :(得分:0)

我的工作代码

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);
            }
        }
    }