限制不可变对象Java中的可变对象

时间:2014-11-28 13:00:23

标签: java immutability

我正在学习不可变的对象。我正在尝试这段代码

  public final class ImmutableObject {

    private final String name;

    private final NormalObject obj =  new NormalObject();

    public String getName() {
        return name;
    }


    public ImmutableObject(String name) {
        this.name = name;
        obj.setName(name);
    }


    public NormalObject getObj() {

        NormalObject tempObj = obj;
        return tempObj;
    }
}

public class NormalObject {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

我想限制调用类更改NormalObject的名称变量的值

但是以下代码更改了值

 ImmutableObject obj = new ImmutableObject("Siddle");

 System.out.println(obj.getObj().getName()); //prints Siddle
 obj.getObj().setName("Kelly");

 System.out.println(obj.getObj().getName()); //prints Kelly

如何限制它?

4 个答案:

答案 0 :(得分:5)

对于不可变的对象,其所有属性必须是不可变的。它的状态一定不能改变。

要做到这一点,你必须在NormalObject上放置一个不可变的外观,你不能直接返回NormalObject。返回它的方法也需要一个不同的返回类型,你不能返回NormalObject但实际上返回的行为不像NormalObject

E.g:

public final class ImmutableObject {

    private final String name;

    private final NormalObject obj =  new NormalObject();

    private final ImmutableNormalObject objFacade = new ImmutableNormalObject(obj);

    public String getName() {
        return name;
    }

    public ImmutableObject(String name) {
        this.name = name;
        obj.setName(name);
    }

    public ImmutableNormalObject getObj() {

        return objFacade;
    }
}

public class NormalObject {

    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

public class ImmutableNormalObject {

    private NormalObject obj;

    public ImmutableNormalObject(Normalobject o) {
        this.obj = o;
    }

    public String getName() {
        return obj.getName();
    }
}

或者,如果复制对象并且它有一个复制构造函数(或者你可以添加一个)是可以接受的,那么你可以这样做,但复制和返回是很昂贵的。

答案 1 :(得分:0)

您可以通过在getter中返回normalObject的副本来执行此操作:

public NormalObject getObj() {
    return new NormalObject(obj.getName());
    // or you can make a copy constructor:
    // return new NormalObject(obj);
}

或者你可以为NormalObject制作一个忽略名字设定器的包装器,但它会制动逻辑。

答案 2 :(得分:0)

请将您的NormalObject代码更改为

public final class ImmutableObject {

    private final String name;
    // initialise it to null
    private final NormalObject obj = null;

    public String getName() {
        return name;
    }


    public ImmutableObject(String name) {
        this.name = name;
        // use the Constructor for setting name only once during initialization of ImmutableObject via its constructor
        obj =  new NormalObject(name);

        //obj.setName(name);
    }


    public NormalObject getObj() {

        NormalObject tempObj = obj;
        return tempObj;
    }
}

NormalObject Class

public class NormalObject {

    private String name;
    public NormalObject(name){
     this.name = name;
    }
    public String getName() {
        return name;
    }
    //Remove any setter on NormalObject
    /*public void setName(String name) {
        this.name = name;
    }*/

}

答案 3 :(得分:0)

在不可变对象中,如果用户尝试更改对象的状态。您将不允许或返回Immutable类的新实例。

因此,由于Date是一个可变的类。 您可以在日期周围创建一个不可变包装器,并且只能公开那些在不可变日期视图中使用的方法,但是您将返回一个不可变类的新实例,并带有新Date的更改属性。

我认为Immutable变量不需要final,因为它已经是私有且不可变的。

示例:

public class Main{

  private ImmutableDate immutableDate;

  public Main() {
    this.immutableDate = new ImmutableDate(new Date());
  }

  public Main(Date date){
    this.immutableDate = new ImmutableDate(date);
  }

  public ImmutableDate getDate() {
    return immutableDate;
  }

  public class ImmutableDate{
    // private constructor, so this can only be instantiated within the outer class
    // therefore, final keyword not required for Date, as no access given to the variable
    private Date date;
    private ImmutableDate(Date date) {
      this.date = date;
    }

    // Example methods from Date, that are required for our Immutable implementation

    public Main setTime(long time){
      Date date1 = new Date();
      date1.setTime(time);
      return new Main(date1);
    }

    @Override
    public String toString() {
      return date.toString();
    }
  }
}