evanjdooner
evanjdooner

Reputation: 904

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?

Upvotes: 2

Views: 326

Answers (5)

Katona
Katona

Reputation: 4901

In my opinion it doesn't. I know that the documentation states that there are optional operations, and even these methods may throw UnsupportedOperationException (which is an unchecked one so the caller might not be prepared for catching it), but I think the purpose of an interface is specifying of what can be done with an implementation, and since those methods are present on the interface, they should be properly implemented. If there are optional operations then there should be some API to discover it by code, not by reading the documentations.

I even think that throwing an UnsupportedOperationException is violating the Liskov substitution principle. Make no mistake, I don't criticize Guava here, this is rather a problem with the standard Java collection framework, the authors of Guava wanted to seamlessly integrate the immutable collections into it and they had to make some compromise.

Upvotes: 0

Hoopje
Hoopje

Reputation: 12932

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.

Upvotes: 9

isomeme
isomeme

Reputation: 471

It's perfectly valid (and in my view, preferable) to use ImmutableSet as the return type. That clues in human readers that what they're getting will be immutable.

Upvotes: 1

rkosegi
rkosegi

Reputation: 14658

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

YES.

Behavior of interface method invocation is implementation specific, until explicitly restricted by documentation. Throwing UnsupportedOperationException is not violation of interface contract.

Example :

java.util.Collections.UnmodifiableList, which implements java.util.List do following:

public void remove() {
    throw new UnsupportedOperationException();
}
public void set(E e) {
    throw new UnsupportedOperationException();
}
public void add(E e) {
    throw new UnsupportedOperationException();
}

If you check javadoc of particular methods of List interface eg, boolean add(E e) you can find following:

  throws UnsupportedOperationException if the add operation
          is not supported by this list

Upvotes: 5

alamar
alamar

Reputation: 19321

Arrays.asList() already returns List that does not permit add().

So I guess returning non-modifiable or partially-modifiable collections are OK, judging that Java spirit is defined by Java standard library specification.

Upvotes: 1

Related Questions