Saturday, 17 January 2009

Item 15: Minimize mutability

Basic rule of this Item: If it is possible: make your class immutable or at least minimize mutability as much as possible because this saves you a lot of trouble.

This Item covers several aspects and effects of mutability, but first the (five) basic rules for immutability:
  • no methods for object state modification / mutatbility
  • ensure that the class can not be extended (either make it final or make all ctors (package)-private and offer static factories, see Item1)
  • - make all field final and private (note, this expresses your intend, however it is still possible to mutate them under certain circumstances)
  • ensure exclusive access to any mutable components
One aspect of immutability is also the functional approach. Returning the result of applying a function and not modifying the operand is one of the key principles in functional programming. FP saves a lot of trouble when it comes to multiple thread, processes, CPUs of whatever granularity you want to name it. I recently learned a functional language and it is really a good experience for every "proecdureal" or "imperative" programmer. Of course for old LISP programmers this is not new stuff, so you are not excited about Erlang, Haskell et all. :-)

Immutable classes (ICs) have several properties which a appreciated:
  • simple (or trivial ? ;-)
  • thread-save out of the box
  • and hence can be shared freely
  • are building blocks for other classes
  • and the JDK has several classes as examples like: String, BigInteger, BigDecimal etc.
On the downside:
  • they should (or have to) be small because...
  • each distinct value requires a separate object
  • in multistep operations you can generate lots of unwanted temporaries
  • somewhere in a program, state changes have to happen, or your program will be very trivial ;-)
Unfortunately there is no annotation (see Item 35 later) or even a simple naming convention to distinguish or mark immutable classes. The only thing is you can hope and read the javadoc for the class. IMO it would make sense to have a standard annotation for immutable classes, despite the fact that the java compiler/annotation checker can not check everything automatically (annotations have some serious limitations) but it would be better than nothing, means no obvious hint (which is the current state in the JDK). I am certain that this would avoid the well known problem that Java Beginners are using Strings to operate with Strings, instead of using StringBuilder or StringBuffer.

IMO a possible standard annotation could check for
  • antipatterns in ICs, e.g. the shall not have a clong method or cctor.
  • ICs shall follow the 5 rules i mentioned in the beginning
  • if your IC has a reference to a mutable object, you have to obey
    serializability (see Item 76)
Regarding the JDK immutable classes there are also other problems, e.g.
  • e.g. String provides a copy constructor but ICs should not have a clone method or cctor.
  • java.util.Date and java.awt.Point should be ICs but are not.
  • String and BigInteger are not so small objects
  • The classes in the JDK can NOT be fixed to be ICs because this would break (binary) backward compatiblity (see my link on the eclipse homepage which describes what you can change and what not, I mentioned this in Item 13)
Josh also outlines some techniques to write performant ICs e.g.:
  • provide a mutable "companion class" for a IC which enables and optimitzes multistep operations This mutable "companion class" can be package-private or package-public. (e.g. BigInteger, String, and their coresponding companions)
and he also shows an alternative approach to make a class "final" e.g.:
  • hide all class ctors and make them private and only offer public static factories (valueOf).
Static factories have several advantages over "normal" ctors, also discussed in Item 1 and in this Item. Unfortunately, AFAIK packages can always be extended by adding other classes (there in no final for packages) so this works only if the clients are outside the packages in question.

My impression of this chapter:
  • pure ICs should be preferred and used if possible
  • try to write ICs and hide mutability in companion classes (which are implementation details and shall not be used by clients)
  • always try to minimized mutability in "normal API classes".
  • there are some defects in JDK ICs as the Java implementors learned also about immutability
  • a standard annotation would a.) communicate the intend forimmutability b.) help us the check the esay cases/rules at least

Bernhard Merkle

No comments: