views:

193

answers:

3

How far back up the implementation hierarchy do I want to go when returning an object?

Using the Java collection interface as an example, would this be appropriate?

Collection outerMethod() {
    return innerMethod();
}

List innerMethod() {
    List list = new ArrayList();
    //do something with the list that requires a method in the List interface
    return list;
}

Or would you want to use List as a return for the outer method?

Another example,

List outerMethod() {
    List list = innerMethod();
    //do something with the list that requires a method in the List interface
    return list;
}

Collection innerMethod() {
    return new ArrayList();
}

An example with parameters,

void outerMethod() {
    innerMethod((List) innerMethodTwo);
}

void innerMethodOne(List list) {
    //do something with the list
}

Collection innerMethodTwo() {
    return new ArrayList();
}

Can anyone offer any general advice?

+5  A: 

Your return type should be as specific as possible to give the consumer of your method the greatest flexibility. In the same spirit it is also best practice to make the types of any parameters as abstract as possible so that consumers of your method also have greater flexibility in what types they can pass in.

Andrew Hare
But that gives you very little flexibility to later change your implementation. It's a balancing act.
Jon Skeet
Very true - it is a balancing act. But by specifying a specific interface as a return type you are at least free to switch up the implementation.
Andrew Hare
Jon, what would you propose as an alternative? Or is it one of those it depends on the situation with no hard and fast rules design choice?
James McMahon
These are both excellent points -- my only addition would be to also consider the final use of your code. Are you creating a public framework? Will you be able to access (and thus refactor) all of your method callers if need be?Intended use is much easier to determine if you currently have consumers instead of determining (guessing at?) flexibility in the future.
micahtan
+2  A: 

I think this is a more general version of this question. The general advice I gave is that there are two rules I follow:

  • Accept the most basic type that will work
  • Return the richest type your user will need
Lee
How could you possibly anticipate what your user would need?
James McMahon
Thank you for the link to that question.
James McMahon
it's hard to anticipate, but that's what sw engineering is all about. :-)That is; choose a reasonable choice based on your best guess; tune as needed (iterative).
StaxMan
+4  A: 

I've found that the answer to the question of how specific to be when exposing types from your class depends on your expectations and restrictions for how callers will interact with your class as well as how your code may evolve. It's a complicated balancing act.

For instance, if you expect callers will need to access elements by index you may return List, but if not then Collection may be sufficient. If you want to restrict callers to only be able iterate over the results and not change them - you want want to return an Iterable<> wrapper instead. If you know that your internal representation may change, you may want to avoid returning very specific classes like List, so that consumers can be more decoupled from your implementation.

Answering these questions is not easy - there is no right or wrong answer - there's also no perfect guidelines. It takes experience, thought, and intuition. And (like most of us), you'll probably get it wrong.

I will say, that personally, I err on the side of being more restrictive in what I expose from my code - especially when it is immature and not yet widely used. Sometimes you can go back and change the return types in your public interface ... sometimes you can't.

LBushkin