Java Immutability for Collection-Typed Properties

Immutability is an important tool for writing more reliable code. Several of the core classes in Java are immutable (String, Integer, etc). But the principle of immutability can also be applied to specific areas of application code to improve the reliability and predictability there.

An immutable object can be trusted to carry unchanging data between different parts of code. If you’re debugging code and you find an immutable object initialized with a certain value, you can be sure that that same object instance will have the same value later. When debugging code, this eliminates one suspect when a wrong value is somehow appearing later in the code.

Consider the case where a class is acting as a service, providing data to code in one or more other areas in a larger application. Say that this service has a method that returns some kind of collection of data, for example:

// In a service class
public List<String> getListOfStringData() {
    return theList;
}

A reference to the instance of the list that’s held internal to the service is being returned to client code. The client code could change the list in some unexpected way unless something is done to protect against it. This could affect any other client code that might also be using this list of data, or the service class itself, perhaps changing its internal state in an unexpected way. Problems could turn up weeks, months, or even years later when the code changes and the existing service is used by another feature of the application.

The java.util.Collections class provides an easy way to protect from this kind of problem. Static “unmodifiable” methods in this class return an instance of a “wrapper” collection class that contains and provides access to the original list:

// In a service class
public List<String> getListOfStringData() {
    return Collections.unmodifiableList(theList);
}

The class returned by the this implements the full java.util.List interface, same as the original mutable list type. The difference is that the wrapper class will throw an exception, java.lang.UnsupportedOperationException (a kind of RuntimeException) if any client code tries to call any method that could change the structure of the underlying collection.

If a client of the list data really has to modify the list (perhaps to sorting it or reversing it), a copy of the list can be made by passing the existing list to one of the standard constructors of the concrete List classes:

// In client code
List<String> changeableList = new ArrayList<String>(theList);

In the case where the contents of the list may change frequently over time, the service class could similarly return a copy of the class . Each client request would get a unique instance of the list that can be changed by the client code as desired.

These “unmodifiable” utility methods apply equally to Lists, Arrays, Maps, and Sets:

Set<SomeType> fixedSet = Collections.unmodifiableSet(originalSet);
Map<KeyType,ValueType> fixedMap = Collections.unmodifiableMap(originalMap);

Concurrency-safety and usefulness as hash keys are often cited as being two important applications of immutability, but predictability among areas of code that share common data is perhaps a more subtle but important way that immutability can improve the quality of code.

TL; DR

In the case where a method of a class is returning an instance of a collection object, wrap the collection in an unmodifiable facade using utility methods in the java.util.Collections class:

public List<String> getListOfStringData() {
    return Collections.unmodifiableList(theList);
}

This way the code that receives the returned collection can’t modify it, making the code more reliable.

Further Reading

java.util.Collections.unmodifiableList()

java.lang.UnsupportedOperationException

Add a Comment