Home » Java » ImmutableList does not extend List?

ImmutableList does not extend List?

Posted by: admin December 28, 2021 Leave a comment

Questions:

When I dig into the gs-collection source for ImmutableList, it does not extends java.util.List. However the class javadoc mentioned that All ImmutableList implementations must implement the java.util.List.

Why must ask the implementation to implement java.util.List and not the ImmutableList itself to extend java.util.List?

Answers:

Why doesn’t ImmutableList extend List?

ImmutableCollection doesn’t extend java.util.Collection (and ImmutableList doesn’t extend java.util.List) because Collection has mutating methods like add() and remove(). If immutable collections had these methods, they would always have to throw UnsupportedOperationException. For users of immutable collections, it would be strange to see add() and remove() in auto-complete choices as callable methods.

Why does the Javadoc impose a contract that all ImmutableList implementations also implement List?

It comes down to equality. An ImmutableList ought to equal a List, assuming both lists have the same contents in the same order. List.equals() imposes a Javadoc contract which states:

Returns true if and only if the specified object is also a list, both
lists have the same size, and all corresponding pairs of elements in
the two lists are equal.

What does it mean that “the specified object is also a list?” We can see in AbstractList.equals() that it means instanceof List.

public boolean equals(Object o) {
    if (o == this)
        return true;
    if (!(o instanceof List))
        return false;
    ...
}

So all ImmutableList implementations must also implement List for equals() to work in a symmetric way. The immutable collection factories already hide implementation details like the fact that an immutable list with a single element is implemented by an ImmutableSingletonList. It also winds up hiding the List interface.

ImmutableList class diagram

Interop

A benefit of this design is that ImmutableList can be cast to List which is important for interop with existing APIs.

// Library method - cannot refactor the parameter type
public void printAll(List<?> list)
{
    for (Object each : list)
    {
        System.out.println(each);
    }
}

ImmutableList<Integer> immutableList = Lists.immutable.with(1, 2, 3);
List<Integer> castList = immutableList.castToList();
printAll(castList);
// also works
printAll((List<?>) immutableList);

// throws UnsupportedOperationException
castList.add(4);

Note: I am a developer on GS Collections.