1

What can you do to make an object such as this immutable? I am mostly concerned about solving public void someMethod(SomeObject someObject) { } <== This case

For example:

public class SomeObject {
   private String someString;

   public void setSomeString(String someString) {
      this.someString = someString;
   }
   public void getSomeString() {
      return someString;
   }
}


public void someMethod() {
   final SomeObject someObject = new SomeObject();
   someObject.set("Want to prevent this"); // <-- SomeObject is mutable in this case
}

public void someMethod(SomeObject someObject) {
   someObject.set("And prevent this!"); // <-- And so is this
}
shinjw
  • 3,329
  • 3
  • 21
  • 42
  • 1
    Set the member variables *only* in the constructor or member declarations. Any object that *can* be modified *after* the constructor returns is not (and cannot be) truly "immutable" - **objects *don't* need setters**. Also, it sounds like the definition of [`final`](http://stackoverflow.com/questions/15655012/how-final-keyword-works) should be reviewed (it has nothing to do with "single reference"). – user2864740 May 12 '15 at 01:43
  • What about a final declaration on an interface of which SomeObject would implement. – shinjw May 12 '15 at 01:49
  • `final` has different meanings, depending on where it is applied (it does *not* relate to immutability when applied to a class; nor to the immutability of objects named by variables). I have no idea what is meant by "final .. on an interface" (although [this question](http://stackoverflow.com/questions/2971881/final-interface-in-java) might be an interesting read). – user2864740 May 12 '15 at 01:50
  • In your example `final SomeObject someObject` the reference variable `someObject` is final and not the object it points to. IMO reference variable cannot point to another object but object can be modified. – akhil_mittal May 12 '15 at 01:57
  • You are correct @akhil_mittal final only allows the reference to be set once – shinjw May 12 '15 at 02:08
  • Since you are provinding mutators, it seems you wish for this object to be mutated in a given context and not in another. If it is the case, your mutators should be package private or protected... of course that would imply that the context in which you dont wish modification would have to be in another package. – Renatols May 12 '15 at 02:17
  • The interface approach would be much more flexible. The only keyword I would be able to use in this situation would be default... which would restrict this to a single package. This is not what I am trying to accomplish. – shinjw May 12 '15 at 02:42

1 Answers1

4

You are right, declaring an object final does not make it immutable. All it does is preventing the final variable from being re-assigned a new object, but the object that is already assigned to it could be mutated if its mutating methods are available.

You can give the object an interface that is immutable, and program to that interface:

interface ImmutableCar {
    String getMake();
    String getModel();
}
class Car implements ImmutableCar {
    public Car(String make, String model) {
        this.make = make;
        this.model = model;
    }
    private String make;
    private String model;
    public String getMake() { return make; }
    public void setMake(String m) { make = m; }
    public String getModel() { return model; }
    public void setModel(String m) { model = m; }
}

If you do it like this

ImmutableCar car = new Car("VW", "Bora");

you would not be able to access mutating methods without an explicit cast. Of course this would not make the object truly immutable, because casting would remain a possibility, but then one could argue that other immutable objects can be changed by going through reflection, so this approach lets you expose only immutable behavior.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • `final ImmutableCar car = new Car("VW", "Bora");` would be able to prevent any recasting... do we have an immutable car? – shinjw May 12 '15 at 01:56
  • 2
    @shinjw Not really: one would be able to write `((Car)car).setMake("Honda")` even if `car` is declared `final`. – Sergey Kalinichenko May 12 '15 at 01:57
  • 1
    I get your point but the question seems a little odd to me. I mean, it could be me, but why would you expose mutators to a class which objects you wish to be immutable? Or should this specific instance be made immutable? Either way, no class can have trully imutable objects if it allows subclassing. – Renatols May 12 '15 at 02:01
  • One case would be an object passed through a method... That object is mutable in this case because that value of the reference is what is actually passed. I agree with using an interface to hide the mutable aspects of the object – shinjw May 12 '15 at 02:03
  • Just adjusted the question. – shinjw May 12 '15 at 02:07
  • @shinjw - no passing a truly immutable object through a method does not make it mutable, if it does then explain for instance `java.lang.String`! –  May 12 '15 at 02:46
  • @JarrodRoberson seems like the question is misunderstood. What I am asking about is the opposite case. How can I make a mutable java object immutable? – shinjw May 12 '15 at 02:50
  • @shinjw - it has been pretty clearly stated you can't as long as the values the references refer to can be altered **in any way** or the objects themselves can change in anyway. *One case would be an object passed through a method... That object is mutable in this case because that value of the reference is what is actually passed.* shows a fundamental lack of comprehension of what the term **mutable** means as well as what *pass by value / pass by reference* means. –  May 12 '15 at 03:00
  • @JarrodRoberson passing an object into a method passes the "pointer" to that object. which means any object method that alters characteristics of that object while in that method in which the object was passed into will actually alter that object... not just within that scope. this is what we're trying to solve. it's to prevent code rot – shinjw May 12 '15 at 03:10
  • @JarrodRoberson using a method signature to take in an interface that restricts access to the "setters" instead of using the object is not a perfect solution but it gets me closer to where I need to be. – shinjw May 12 '15 at 03:17