Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

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?

share|improve this question
3  
Did you happen to notice in java.security.cert that one return type is Collection<List<?>>? Talk about coding horror! –  Macneil Nov 26 '10 at 19:44
add comment

3 Answers 3

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)

share|improve this answer
    
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). –  unsquared Nov 26 '10 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 '10 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. –  unsquared Nov 26 '10 at 20:53
add comment

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.

share|improve this answer
    
+1 for pointing out its usefulness as a parameter. –  unsquared Nov 26 '10 at 19:50
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 '10 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. –  Tom Anderson Nov 26 '10 at 19:54
    
@Sparkie: Thanks; I've updated my answer. –  Macneil Nov 27 '10 at 18:00
    
@Tom: Good point! –  Macneil Nov 27 '10 at 18:02
add comment

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.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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