mvg
mvg

Reputation: 1584

Restrict mutable object inside immutable object Java

I am learning about immutable Objects. I am trying this code

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

I want to restrict the calling class from changing the value of name variable of NormalObject

But the following code changes the value

 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

How to restrict it?

Upvotes: 3

Views: 3729

Answers (4)

aayoustic
aayoustic

Reputation: 109

In an immutable object, if a User tries to change the state of the Object. either you won't allow or return a new Instance of the Immutable class.

So, since Date is a mutable class. You can create an immutable wrapper around date, and you can expose only those methods, that are subject to be used in your Immutable-Date's perspective, but you return a new instance of your Immutable class, with the changed attribute of your new Date.

I don't think final would be required for Immutable variable, because it is already private and Immutable.

Example :

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

Upvotes: 0

suntossh
suntossh

Reputation: 1

Please change Your NormalObject code to

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

}

Upvotes: 0

alaster
alaster

Reputation: 4181

You can do this by returning a copy of your normalObject in getter:

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

Or you can make a wrapper for your NormalObject that ignores name setter, but it brakes logic.

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1075755

For an object to be immutable, all of its properties must be immutable. Its state must not be changeable.

To do that, you have to put an immutable facade on NormalObject, you can't directly return a NormalObject. The method that returns it will also need a different return type, you can't return NormalObject but actually return something that doesn't behave like a 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();
    }
}

Alternately, if it's acceptable to copy the object and it has a copy constructor (or you can add one), you could do that, but copy-and-return is expensive.

Upvotes: 5

Related Questions