Wednesday, October 31, 2007

Immutable objects in Java

I just read a clever post from JP Moresmau on a functional language he is writing which translates to Java.

One idea which lept out at me was how to provide setters to immutable objects in Java: the setters are instance factory methods, thus:

class DontTreadOnMe {
    public final String species;
    public final int length;

    DontTreadOnMe(String species, int length) {
        this.species = species;
        this.length = length;
    }

    // Snakes can grow but cannot change species
    DontTreadOnMe setLength(int length) {
        return new DontTreadOnMe(length);
    }
}

This works rather nicely for code which uses the convention. Unfortunately, the getter/setter idea is so firmly embedded in the fingertips of Java coders, that I expect this bug frequently:

final DontTreadOnMe copperhead
        = new DontTreadOnMe("Copperhead", 1);
// Grow in length
copperhead.setLength(2);
// Oops! Does not work as expected but compiles

My preference is for the bean properties proposal. Make immutables with read-only properties; use copy-construction to make new objects with different field values (fortunately Java copy construction is vastly simpler than C++).

5 comments:

Nat Pryce said...

For methods like this, I prefer the prefix "with" instead of "set". After all, these methods are *not* setters in the way most Java programmers would expect.

Anonymous said...

It should not compile because constructor with one int as argument is missing. Probably it would make sense to call existing constructor and give also species as parameter.

Brian Oxley said...

Mikko, what about the field, 'species'?

Anonymous said...

Because it is not static field, new object do not have it set. If purpose is to have new instance with same species, but with new length, following should work:
// Snakes can grow but cannot change species

DontTreadOnMe setLength(int length) {
return new DontTreadOnMe(this.species, length);
}

Brian Oxley said...

Mikko, this is certainly true. I just prefer the copy-constructor idiom from C++ for the purpose I have in mind. A matter of taste, to be sure. In non-example code I would have describe this idiom in the javadocs for the sake of those not familiar with it.