Monday, 12 September 2011

Public final is also Immutable, Again

Dear Junior

I must apologise as in my last letter I mixed two separate discussions into one: one about immutability, and one about encapsulations. What I was after was immutability, even though encapsulation is also interesting.

To make thinks clear, let us return to the Name class for representing value objects for a name e g "Dan Bergh Johnsson". To focus on immutability, let us simplify it.

public class Name {
    public final String fullname;

    public Name(String fullname) {
        this.fullname = fullname;
    }
}

Now, if I create an object of this class, then no client can mutate the state of that object. This is because

  1. The datafield is final so the client cannot make the reference point to some other String 
  2. Strings are immutable, so the referred object cannot be changed 

Just for reference. I mentioned that Scala has a very elegant way of defining "public immutable datafield properties". The corresponding Scala class would be a one-liner.

class Name(val fullname: String)

Elegant, isn't it? "Name is a class with the attribute value 'fullname' of type String". Scala promotes the use of immutable constructs by making it simple to declare them.

Now, as my friend Tommy Malmström pointed out to me, I should also make my class final. This is a very valid and interesting point, so let me dig into.

The problem in this case is not the objects we construct ourselves, but objects that are sent to us. We should rightfully assume that such objects are also immutable. However, someone could wittingly and deviously create a mutable subclass of Name.

Back in Java land such a subclass could for example overrride toString

package names;

class EvilName extends Name {
    public EvilName(String fullname) {
        super(fullname);
    }

    public String toString() {
        return "Voldemort";
    }

    public static void main(String[] args) {
        Name name = new EvilName("Harry Potter");
        System.out.println(name);
        System.out.println(name.fullname);
    }
}

danbj$ java names.EvilName
Voldemort
Harry Potter

You see how confusing it could be when toString is used to render the name object "Harry Potter".

So, forbidding such overrides by declaring the Name class as final is really a good idea.

On a side note, this is the reason why String is declared final. The String class is for example used to represent class and package information when loading code dynamically over the network - so imagine the consequences had it been possible to make a mutable phoney String.

Well, that was about immutability.

On the issue of encapsulation it can be argued whether "public final String fullname" breaks encapsulation or not - and that discussion also depends on what encapsulation we mean. Any way, that is a separate discussion.

Yours

   Dan

2 comments:

  1. I find creating truly immutable objects in Java really difficult.

    If the member is a String, most people know it's immutable, but what about if you have a Person who has a Name and the internal representation in the Name is a String (and the name datafield is it's only member).

    Should the Name getName() method in the Person try to explicitly return a new Name (by cloning the existing Name object — often difficult and error prone) or should you just return the existing Name because you know the internal representation is an immutable String anyways.

    Both strategies lead to difficult situations.

    Trying to abstract away the internal representation and defensively guard immutability leads to lots of awful deep-copying of object hierarchies.

    Choosing to just rely on that the internal String representation is immutable works, until someone changes the String to another type. It also becomes a bit unclear if the developer really wants to maintain immutability.

    It gets a lot worse with collections.

    I'm mostly venting as I've recently really tried to create an API that only uses immutable objects. Found no solace in my ordinary Java bible, Joshua Blochs, Effective Java.

    ReplyDelete
  2. Dear Erik

    I do certainly agree. At one point of time I had a team-fellow change the immutable Money-class to have a "setAmount" method (he did write tests first, though). I spotted that change by accident the day after, unchanged the class and adapted the client code. But imagine the bugs it could have resulted in!

    And it certainly becomes worse with Java collections. Even if there is an "immutable mode" on them, they are obviously designed to be mutable.

    I guess the only way to guarantee immutability would be code inspection. We know that String, Integer, and a few other are immutable. We also know that if a class is final and every field is declared as a final reference to an immutable class, then that class is also immutable.

    So we have an inductive definition of classes we know are immutable, so there is an obvious recursive way of checking a class. Using reflection we could automate that check and perform it compile-time. I guess it could help if we had an annotation "@immutable-inductive" we could use to tag our (intended) immutable classes, and for which we should run the check.

    It does not seem too tricky, but I have not done it; and probably the devil is in the details.

    Yours

    Dan

    ReplyDelete