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

3

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

  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的name变量的值

但是以下代码会更改该值

 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个回答

5

要使一个对象不可变,它的所有属性都必须是不可变的。它的状态不能被改变。

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

例如:

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

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


我能把你的代码看作是Facade设计模式的一个例子吗?https://dev59.com/9m435IYBdhLWcg3wvys5 - mvg
1
@mvg:是的,这是该模式的一个示例。 - T.J. Crowder

0

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

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

或者你可以为你的NormalObject创建一个包装器,忽略名称设置器,但这会破坏逻辑。


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

普通对象类

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;
    }*/

}

我希望NormalObject保持不变。我只想限制ImmutableObject的变量。 - mvg

0
在不可变对象中,如果用户尝试更改对象的状态,则可以选择不允许或返回一个新的不可变类实例。
因此,由于日期是一个可变类,您可以创建一个不可变包装器来包装日期,并仅公开那些可能在您的不可变日期视角中使用的方法,但您需要返回一个新的不可变类实例,其中包含新日期的更改属性。
我认为对于不可变变量,不需要使用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();
    }
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接