Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Mutable or immutable?

Techniques for defensive programming

  • Print
  • Feedback

December 6, 2002

Q How do I create my own pair of mutable and immutable classes similar to String and StringBuffer?

A Before I jump into the details of my answer, let me first point out that there are flavors of immutability.

Immutability in a "weak" sense (for lack of a better term) means creating a temporary read-only view of otherwise modifiable data. A typical case is implementing an accessor method: one must avoid giving out writable handles to internals of the class implementation. Note that an accessor is not necessarily expected to return the same data every time it is called. The key is not letting the caller subvert the read-only access protocol in place of supported mutator methods.

Immutability in a "strong" sense means locking down a piece of data in perpetuity, such as creating an immutable object instance that cannot be changed by any code. Knowing that such an instance will never change opens doors for a host of other useful patterns. For example, when cloning an object with String fields, it is not necessary to clone them since Strings are immutable.

All these immutability flavors are invaluable for good defensive programming. And since Java does not provide much native language support in this area, developers must take matters in their own hands. Fortunately, it is not very complicated.

I illustrate a couple of patterns for separating a class API into mutable and immutable parts here.

Immutable class extended by a companion nonpublic mutable class

Consider these two classes:

package items;
public class Item
{
    public Item (int a, int b)
    {
        m_a = a;
        m_b = b;
    }
    // ACCESSORS: [all methods are final]
    
    public final int getA ()
    {
        return m_a;
    }
    
    public final int getB ()
    {
        return m_b;
    }
    
    int m_a, m_b;
}
package items;
final class MutableItem extends Item
{
    public MutableItem (int a, int b)
    {
        super (a, b);
    }
    
    // MUTATORS:
    
    public void setA (int a)
    {
        m_a = a;
    }
    
    public void setB (int b)
    {
        m_b = b;
    }
}


Item is immutable: it has no mutator methods. It is further specialized into MutableItem that adds mutators. Conceptually, this inheritance relationship makes sense: being able to read and write is of course more than just being able to read.

Anything that has a MutableItem instance fully controls its state. However, this access can be restricted to read-only by upcasting such an instance to Item, as demonstrated by this simple class:

package items;
public class ItemArray
{
    public ItemArray (int size)
    {
        m_items = new MutableItem [size];
        for (int i = 0; i < size; ++ i) m_items [i] = new MutableItem (0, 0);
    }
    public final Item getItem (int index)
    {
        return m_items [index]; // no data copying necessary
    }
    public final void setItem (int index, int a, int b)
    {
        // Update the existing item:
        m_items [index].setA (a);
        m_items [index].setB (b);
    }
    
    private final MutableItem [] m_items;
}


ItemArray.getItem() exposes a mutable object in an immutable way. Note that this method has no data copying and no new object allocation, and the overall implementation is very efficient and safe. Downcasting the result of getItem() to its mutable subclass is not possible outside the items package because the mutable class is package-private. And even when this restriction does not apply, such as in my own code in the same package, downcasting needs to be coded explicitly and will trigger a mental check: should I be doing this? This weak immutability pattern is a bit like const-correctness in C++, where an object's "const-ness" is enforced by the compiler and can be cast away with an explicit directive.

  • Print
  • Feedback

Resources