13

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?

2
  • 3
    Did you happen to notice in java.security.cert that one return type is Collection<List<?>>? Talk about coding horror!
    – Macneil
    Nov 26, 2010 at 19:44
  • 1
    @Macneil I don't know which class you a referring to, but such a return type could indeed be quite sensible. It essentially tells you that you have a collection (i.e. containing a bunch of things that does not have a sensible ordering) of lists (i.e. containing things that have a sensible ordering) of objects (i.e. items whose types we don't know statically for whatever reason). Doesn't seem unreasonable to me.
    – Zero3
    Dec 15, 2015 at 15:16

3 Answers 3

14

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.

6

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.

3
  • 2
    I think that second point is a bit weak. You do not know how the collection is going to perform regardless of whether it's Collection or List - since they're just abstractions. Unless you have a concrete final class, you can't really tell.
    – Mark H
    Nov 26, 2010 at 19:52
  • Also, if all you know is that something is a Collection, you have no idea if it can contain duplicates or not. Turning that around, once case where returning Collection might be appropriate is if you have a collection which does not contain duplicates and has no significant order (which would naturally be a Set), but where for some good reason, the implementation of the returning method uses a List. You wouldn't want to return a List, because that implies significant order, but you can't return a Set without going through the rigmarole of making one. So you return a Collection. Nov 26, 2010 at 19:54
  • @Tom: Good point!
    – Macneil
    Nov 27, 2010 at 18:02
5

I'd look at it from the complete opposite point of view, and ask:

If you came across code that employed List<> when doing a code review, how would you determine whether its usage is justified?

It's quite easy to justify this. You use the List when you need some functionality that is not offered by a Collection. If you don't need that extra functionality - what justification do you have? (And I won't buy, "I'd prefer to see it")

There are plenty of cases where you will be using a collection for read-only purposes, populating it all at once, iterating through it entirely - do you ever need to index the thing manually?

To give a real example. Say I perform a simple query on a database. (SELECT id,name,rep FROM people WHERE name LIKE '%squared') I grab back the relevant data, populate Person objects and put em into a PersonList)

  • Do I need to access by index? - Would be meaningless. There's no mapping between index and ID.
  • Do I need to insert at an index? - No, the DBMS will decide where to put it, if I'm adding at all.

So what justification would I have for those extra methods? (which would be left unimplemented in my PersonList anyway)

3
  • Fair point. I suppose my question refers to specific instance where, while doing a code review, I keep seeing DAOs that return Collections, but I know that these DAO calls are always going to be returning sets of entities; my contention is that in these cases the return type indicates uniqueness, and this information is helpful to whoever has to use that method (e.g. I don't have to check for duplicates).
    – Fil
    Nov 26, 2010 at 20:16
  • If you've queried a DB, comparing 2 resulting objects with equals() shouldn't produce true at all - so you'd need another way of comparing the objects for duplication (eg, do they have the same name, same ID, both?). You'd need to tell your DAO how to compare them if it is to remove duplicates itself - but since you're the user who's deciding whether duplicates exist - it's easier to just do it with the collection from the calling code. (To avoid more layers of abstraction to inform the DAO how to perform every possible equality check on the planet.)
    – Mark H
    Nov 26, 2010 at 20:27
  • Agreed, but we're using Hibernate, and ensure that our entities implement equals(). So when a DAO is returning entities, we can very quickly do new HashSet().addAll(results) and return that back to the called method.
    – Fil
    Nov 26, 2010 at 20:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.