Pages

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