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.
programming practices - Is there a good reason to use Java's Collection interface? - Software Engineering Stack Exchange
java - Collection Interface vs arrays - Stack Overflow
Java's Collections Framework Gets a Makeover with New Sequenced Collection Types
Java ImmutableCollection inheriting from Collection interface! Bad decision by Java team?
Videos
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.
The Collection interface, and the most permissive form Collection<?>, is great for parameters that you accept. Based on use in the Java library itself it's more common as a parameter type than a return type.
For return types, I think your point is valid: If people are expected to access it, they should know the order (in the Big-O sense) of the operation being performed. I would iterate over a Collection returned and add it to another Collection, but it would seem a bit crazy to call contains on it, not knowing if it's a O(1), O(log n), or O(n) operation. Of course, just because you have a Set doesn't mean it's a hashset or a sorted set, but at some point you will make assumptions that the interface has been reasonably implemented (and then you'll need to go to plan B if your assumption is shown to be incorrect).
As Tom mentions, sometimes you need to return a Collection in order to maintain encapsulation: You don't want implementation details leaking out, even if you could return something more specific. Or, in the case Tom mentioned, you could return the more specific container, but then you'd have to construct it.
The easy way to think of it is: Collections beat object arrays in basically every single way. Consider:
- A collection can be mutable or immutable. A nonempty array must always be mutable.
- A collection can allow or disallow null elements. An array must always permit null elements.
- A collection can be thread-safe; even concurrent. An array is never safe to publish to multiple threads.
- A list or set's
equals,hashCodeandtoStringmethods do what users expect; on an array they are a common source of bugs. - A collection is type-safe; an array is not. Because arrays "fake" covariance,
ArrayStoreExceptioncan result at runtime. - A collection can hold a non-reifiable type (e.g.
List<Class<? extends E>>orList<Optional<T>>). An array will generate a warning for this. - A collection can have views (unmodifiable, subList...). No such luck for an array.
- A collection has a full-fledged API; an array has only set-at-index, get-at-index, length and clone.
- Type-use annotations like
@Nullableare very confusing with arrays. I promise you can't guess what@A String @B [] @C []means. - Because of all the reasons above, third-party utility libraries should not bother adding much additional support for arrays, focusing only on collections, so you also have a network effect.
Object arrays will never be first-class citizens in Java APIs.
A couple of the reasons above are covered -- but in much greater detail -- in Effective Java, Third Edition, Item 28, from page 126.
So, why would you ever use object arrays?
- You're very tightly optimizing something
- You have to interact with an API that uses them and you can't fix it
- so convert to/from a
Listas close to that API as you can
- so convert to/from a
- Because varargs (but varargs is overused)
- so ... same as previous
- Obviously some collection implementations must be using them
- I can't think of any other reasons, they suck bad
It's basically a question of the desired level of abstraction.
Most collections can be implemented in terms of arrays, but they provide many more methods on top of it for your convenience. Most collection implementations I know of for instance, can grow and shrink according to demand, or perform other "high-level" operations which basic arrays can't.
Suppose for instance that you're loading strings from a file. You don't know how many new-line characters the file contains, thus you don't know what size to use when allocating the array. Therefore an ArrayList is a better choice.