5

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

5 Answers 5

8

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.

11
  • 4
    Re: "A Class isn't truly immutable if any of this children references aren't immutable as well", I don't think that's quite true. It depends on the semantics of the class and how it actually uses the non-immutable object. It may be OK for an "immutable" DocumentResultSet to have references to mutable Document objects, as long as those references are final.
    – Darien
    Jun 22, 2011 at 22:09
  • 8
    This is a good answer, but why do you have to bold every single instance of mutable and immutable? Really makes it harder to read.
    – Aaronaught
    Jun 23, 2011 at 3:05
  • 1
    @Darien I sort of agree. For example, I like to implement immutable lazy lists (handy for parsing) using an Iterator (the source), an ArrayList (elements that have been read from the source), and a position. In a sense the class is mutable because it references two mutable objects, but the mutability is completely encapsulated; the class fulfills the contract of an immutable structure. Jun 23, 2011 at 4:01
  • 4
    please correct your answer to reflect the fact that an immutable object can reference mutable objects as long as: 1. The references to its are not leaked; and 2. the immutable object cannot modify the mutable objects after construction. (Java Concurrency in Practice, Section 3.4 Immutability, Goetz2006) Jun 29, 2011 at 14:23
  • 4
    A class can hold references to mutable objects and still be considered immutable if the purpose of the references is to IDENTIFY rather than CONTAIN the objects referred to thereby. For example, an immutable list of cars would be one that always refers to the same automobiles. If the fifth car on the list is a red car with VIN #24601, it might tomorrow be a blue car with VIN #24601, but it won't have any other VIN#.
    – supercat
    Jul 17, 2012 at 21:01
2

It doesn't really matter as long as the class you say is immutable cannot be observed to be mutable. E.g., memoisation uses mutation but a memoised value can be treated as immutable for all intents.

If I can't tell that your object uses mutable variables internally then it doesn't compromise my program and I can compose it with other objects as usual.

0

Immutability is useful when designing concurrent applications because of the object state guarantees it provides. It's also quite useful for Sets and as Map keys... As far as i am concerned there's no choice between immutability and mutability. As an example, in a multithreaded Bank application, an Account object cannot be made immutable because its a dynamic entity which changes overtime and its unique to one holder. However a Discount or APR can be designed as an Immutable object because they do not vary beyond definition. In short your problem domain will decide how you model your objects. Too many times Java programmers write code for "Gang Of Four" as opposed to coding to customer's requirement.

0

Well, the concept is reading the JLS and understanding it. In this case, the JLS says:

final fields also allow programmers to implement thread-safe immutable objects without synchronization. A thread-safe immutable object is seen as immutable by all threads, even if a data race is used to pass references to the immutable object between threads. This can provide safety guarantees against misuse of an immutable class by incorrect or malicious code. final fields must be used correctly to provide a guarantee of immutability.

The usage model for final fields is a simple one: Set the final fields for an object in that object's constructor; and do not write a reference to the object being constructed in a place where another thread can see it before the object's constructor is finished. If this is followed, then when the object is seen by another thread, that thread will always see the correctly constructed version of that object's final fields. It will also see versions of any object or array referenced by those final fields that are at least as up-to-date as the final fields are. So you need to:

Make address both final and private. Prevent any mutable reference to that object from being seen externally to your immutable object. In this case, #2 probably means you can't return a reference to Address like you have with getAddress(). And you have to make a defensive copy. Make a copy of the object, and store that copy in Employee. If you can't make a defensive copy, there's really no way to make Employee immutable.

public final class Employee{
    private final int id;
    private final Address address;
    public Employee(int id, Address address)
    {
        this.id = id;
        this.address=new Address();  // defensive copy
        this.address.setStreet( address.getStreet() );
    }
    pulbic int getId(){
        return id;
    }
    public Address getAddress() {
        Address nuAdd = new Address(); // must copy here too
        nuAdd.setStreet( address.getStreet() );
        return nuAdd;
}

Implementing clone() or something similar (a copy ctor) would make creating defensive objects easier for complicated classes. However, the best recommendation I think would be to make Address immutable. Once you do that you can freely pass around its reference without any thread-safety issues.

In this example, notice I do NOT have to copy the value of street. Street is a String, and strings are immutable. If street consisted of mutable fields (integer street number for example) then I would have to make a copy of street also, and so on ad infinitum. This is why immutable objects are so valuable, they break the "infinite copy" chain.

0

Well, simply put; can your state change?

This is not just through your objects API, but in every way an object can change state.

  • You can't leak mutable references.
  • You can't inject mutable references.
  • You can't change state on any references, not even if the object is completely self contained.

You can initialize a mutable object, as long as no calls are ever made that change state, but you can still query it. So, if internal mutability is not what I described here, then no, you can't have that.

Not the answer you're looking for? Browse other questions tagged or ask your own question.