Reputation: 904
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
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
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:
Upvotes: 9
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
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
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