1

Suppose you have a class Dog, that has

public class Dog {

    private String name;
    private double age;

    // some setters
    // some getters

Additionally, you have a class DogHandler, that makes an instance of the Dog d and passes it to Owner

I suppose, i can

... make a copy of a Dog before passing it to Owner, but that's an expensive operation and i'd rather avoid it.

... come up with an interface that Dog implements which contains getters only, cast Dog to that interface and pass the result along

... initialize settable variables in a constructor and simply not allow changes for this instance of an object

Are there any other ways to make sure receiver of the object cant modify it?

How do you take a simple bean containing some data and make it read-only?

James Raitsev
  • 92,517
  • 154
  • 335
  • 470
  • Take a look at [this post](http://stackoverflow.com/questions/5124012/immutable-classes) on immutable classes. – Maciej Sep 21 '11 at 20:05
  • I find creating a base inteface that clients deal with like `Dog` for example and then a concrete implementation `MutableDog` which has setters works well – Ciaran Liedeman Jul 05 '12 at 13:34

4 Answers4

2

This can be achieved in few ways, I can propose you 2 of them:

a) interface with getters is good idea

b) create derived class from Dog which has setters method blocked, like this:

class UnmodifiedDog extends Dog {
    public UnmodifiedDog(double age, String name) {
        super.setAge(age);
        super.setName(name);
    }
    @Override
    public void setAge(double age) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void setName(String name) {
         throw new UnsupportedOperationException();
    }   
}

In DogHandler:

Dog createDog() {
    return new UnmodifiedDog(10, "Fido");
}

and you can pass this to the Owner:

owner.receiveDog(dogHandler.createDog());
lukastymo
  • 26,145
  • 14
  • 53
  • 66
  • Why not to use existing `Dog` class as data source instead of passing age and name parameters to UnmodifiedDof constructor? That is like with `Collections.unmodifyableList` – michael nesterenko Sep 21 '11 at 20:48
  • @Misha, This is important if you have a lot fields to set, like in Lists or other collections. In this case I have only 2 fields, so it doesn't matter if I pass Dog in constructor or just 2 fields. But for more fields, yeah, your proposition is better approach – lukastymo Sep 21 '11 at 21:01
0

The approaches you mention in the question are pretty much the standard steps to take to make Dog immutable. The only other tip would be to mandate that Dog cannot be overridden by declaring the class to be final.

avik
  • 2,708
  • 17
  • 20
  • change class to final won't work in this case, you can still change what is inside Dog: age and name. – lukastymo Sep 21 '11 at 20:26
  • @smas, you're right. I digressed and started talking about immutable _classes_, whereas the original question was about immutable _objects_. – avik Sep 21 '11 at 21:15
0

Among the solutions mentioned here, you can also take advantage of visibility modifiers. If Dog and Owner are in separate packages, you can set the visibility of the mutators to default (package) scope or protected scope.

This will allow you to keep Dog and DogHandler in the same package (and therefore allow them both to mutate the Dog object accordingly), while keeping Owner objects separate (and therefore preventing them from making any modification to the Dog objects).

Jon Newmuis
  • 25,722
  • 2
  • 45
  • 57
0

Here is an example using an interface and package access setters.


package blah.animal;
public interface Dog
{
  double getAge();
  String getName();
}

package blah.animal;
public class DogImpl implements Dog
{
  private double age; // double seems wrong for age.
  private String name;

  ... getters (defined by Dog interface)

  // package access setters.
  void setAge(double newValue)
  {
    age = newValue;
  }

  void setName(String newValue)
  {
    name = newValue;
  }

  package blah.animal;
  public class DogHandler
  {
    public static Dog newDog(double age, String name)
    {
      Dog returnValue = new DogImpl();
      returnValue.setAge(age);
      returnValue.setName(name);

      return returnValue;
    }
  }

  package.blah.somethingelse;
  public class Blam
  {
    private Dog myDog;

    public Blam()
    {
      myDog = DogHandler.newDog(1.4D, "Tippy");
    }
  }
DwB
  • 37,124
  • 11
  • 56
  • 82