What are the consequences of immutable classes with references to mutable classes

concurrencyimmutability

I've recently begun adopting the best practice of designing my classes to be immutable per Effective Java [Bloch2008]. I have a series of interrelated questions about degrees of mutability and their consequences.

I have run into situations where a (Java) class I implemented is only "internally immutable" because it is immutable except that it uses (immutable) references to other mutable classes. To clarify, if the object were constructed with its references set to immutable classes, the class would be immutable.

  1. Do any of the benefits (see below) of immutable classes hold true even by only "internally immutable" classes?

  2. Is there an accepted term for the
    aforementioned "internal mutability"?
    Wikipedia's immutable object
    page
    uses the unsourced term
    "deep immutability" to describe an
    object whose references are also
    immutable.

  3. Is the distinction between mutability and
    side-effect-ness/state important?


The "Java Practices" site lists the following benefits of immutable classes:

  • are simple to construct, test, and
    use

  • are automatically thread-safe and
    have no synchronization issues

  • do not need a copy constructor

  • do not need an implementation of
    clone

  • allow hashCode to use lazy
    initialization, and to cache its
    return value

  • do not need to be copied defensively
    when used as a field

  • make good Map keys and Set elements
    (these objects must not change state
    while in the collection)

  • have their class invariant
    established once upon construction,
    and it never needs to be checked
    again

  • always have "failure atomicity" (a
    term used by Joshua Bloch) : if an
    immutable object throws an exception,
    it's never left in an undesirable or
    indeterminate state

Best Answer

A Class isn't truly immutable if any of this children references aren't immutable as well. If your root Class has final references to all its instance variables and all those instance variables are immutable as well as all their children following this same restriction then you can say the root Class is immutable.

If any of the children references are non-final or any of the children instances are mutable then the containing Class is not immutable.

It doesn't matter about the internal immutablity whatever that means is anyone's guess, all that matters is that the public contract to the class immutability is kept. You can't have partial immutability anymore than you can be partially dead. Your class is either immutable or it isn't. And it isn't if any of its referred to classes or their classes, etc. are mutable.

If you have a graph and any of the members of the graph are mutable you can't deterministically say that you have an immutable state. Concurrency guarantees go out the window, .equals() and .hashCode() become non-deterministic and simple to test cloning and serializing go out the window as well.

If anything breaks this immutability contract you lose all the benefits of trying to maintain this immutability contract.

Simpler concurrency issues is a main motivational force of immutability.

Having side effect free code helps with predictability and maintainability. Since you don't have to wonder where things are getting mutated in the call tree because you know they can't be mutated.

Performance is another less important factor, in Java the JVM can make some highly optimized decisions about caching and other factors if it knows data can't change state. It does provide important hints to the compiler at compile time and the JIT algorithms at runtime.

Related Topic