Search code examples
javacollectionsinterfaceguavaimmutability

Do the Guava Immutable* classes satisfy the standard collection interfaces they implement?


For instance, if I have an interface like

public interface Partition<E> {
    Set<E> getIncluded();
    Set<E> getExcluded();
}

And I implement it like so

public class ImmutablePartition<E> implements Partition<E> {
    private final ImmutableSet<E> included;
    private final ImmutableSet<E> excluded;

    ImmutablePartition(Set<E> included, Set<E> excluded) {
        this.included = ImmutableSet.copyOf(included);
        this.excluded = ImmutableSet.copyOf(excluded);
    }

    @Override 
    public Set<E> getIncluded() {
        return included;
    }

    @Override
    public Set<E> getExcluded() {
        return excluded;
    }
}

Am I really implementing the spirit of the original interface? If client code gets a Set<E> back, tries to manipulate it, and gets an UnsupportedOperationException, surely that defeats the purpose of implementing the Set<E> interface in the first place?

This question applies to all Guava Immutable* collections that implement the standard collection interfaces from java.util.

edit: As I've been reminded below, the Collection interface specifies the UnsupportedOperationException for unsupported mutation methods. I feel like the expectation still is that a returned collection will allow modification, unless otherwise stated. If I wanted to return an immutable collection, I would specify the return type as the immutable class, if possible.

I guess my question is: being that the usual assumption (in my experience) is that a returned collection will be mutable, is it sensible to implement a interface method that returns a general collection and return an immutable collection?


Solution

  • I don't know what the spirit of an interface is, but the specification (a.k.a. Javadoc ;-) of the Java collection interfaces clearly state that immutable collections may throw an UnsupportedOperationException when the user tries to modify them.

    Edit to also answer your edited question

    First, I agree that whether a returned collection is mutable should be documented. However, I disagree that the default assumption is that the collection is mutable. Also when a returned collection is mutable I expect that this is documented, and also that it is documented what happens when I modify the collection (in particular: is the object from which the collection originates modified when I modify the collection, or is it just a copy of some data).

    It would maybe be good to have types like ImmutableSet, ImmutableList, and so on, but the Java standard library doesn't have them. The reason is that the situation is not a boolean: a collection can be partially modifiable, for example because it allows deleting elements but not adding new ones. It would not be a good idea to have seperate interfaces for all possible combinations, and therefore the Java designers decided to have none.

    You can use an external library, such as Guava, but this also has disadvantages:

    • You introduce a dependency to an external library
    • The Guava immutable collections contain a copy of the original data, not a view. If your class just wants to make some internal list public without the possibility that the caller changes the internal data, making a copy every time is probably not what you want.