Java – good reason to use Java’s Collection interface

javaprogramming practices

I've heard the argument that you should use the most generic interface available so that you're not tied to a particular implementation of that interface. Does this logic apply to interfaces like java.util.Collection?

I would much rather see something like the following:

List<Foo> getFoos()

or

Set<Foo> getFoos()

instead of

Collection<Foo> getFoos()

In the last case, I don't know what kind of data set I'm dealing with, whereas in the first two instances I can make some assumptions about ordering and uniqueness. Does java.util.Collection have a usefulness outside of being a logical parent for both sets and lists?

If you came across code that employed Collection when doing a code review, how would you determine whether its usage is justified, and what suggestions would you make for its replacement with a more specific interface?

Best Answer

Abstractions live longer than implementations

In general the more abstract your design the longer it is likely to remain useful. So, since Collection is more abstract that it's sub-interfaces then an API design based on Collection is more likely to remain useful than one based on List.

However, the overarching principle is to use the most appropriate abstraction. So if your collection must support ordered elements then mandate a List, if there are to be no duplicates then mandate a Set, and so on.

A note on generic interface design

Since you're interested in using the Collection interface with generics you may the following helpful. Effective Java by Joshua Bloch recommends the following approach when designing an interface that will rely on generics: Producers Extend, Consumers Super

This is also known as the PECS rule. Essentially, if generic collections that produce data are passed to your class the signature should look like this:

public void pushAll(Collection<? extends E> producerCollection) {}

Thus the input type can be E or any subclass of E (E is defined as both a super- and sub-class of itself in the Java language).

Conversely, a generic collection that is passed in to consume data should have a signature like this:

public void popAll(Collection<? super E> consumerCollection) {}

The method will correctly deal with any superclass of E. Overall, using this approach will make your interface less surprising to your users because you'll be able to pass in Collection<Number> and Collection<Integer> and have them treated correctly.