For example, if I have a function like
public List<E> sort(ArrayList<E> list) {...}
My classmate told me, we should use List as return type because return type should be as broad as possible. His reason here is we can change to return a LinkedList or another type later if we currently return an ArrayList. It increases the flexibility.
But the function parameter we receive should be as narrow as possible. This is because we may need to use some function that only implemented by ArrayList in our sort function.
So my question is this: is there any widely accepted guidelines/best practices that recommend that formal parameters of a function should be as narrow as possible typed? If not, what would be the main arguments against?
=============================================
Just to clarify that.
public List<E> sort(ArrayList<E> list) {...}
This is just an example, the List and ArrayList could be replaced by any other Super Class and SubClass. Let's assume there are some functions only exist in the SubClass.
public <E> SuperClass<E> sort(SubClass<E> object) {...}
Most programmers will agree that a return as broad as possible is a good thing (due to the flexibility factor) you describe.
I believe many will argue that the as-broad-as-possible guideline holds also for parameters as it allows your function to be used from a broader spectrum: if in some place in the code there is a LinkedList
object that that place will be able to call your method (if it needs to sort) if it were defined with a parameter of type List
, but it would not be able to call your method if it were defined with a parameter of type ArrayList
.
So in general, as broad as possible is a good guideline. Of course, it is up to you (as the author of the method) to determine what "broad as possible" mean for you. Specifically, if you could make it broader but with some cost (e.g., a more complicated implementation) you have to decided whether you are willing to accept this additional complexity (higher chances of bugs) in return for the broader applicability.
To apply the generics correctly, you should both accept and return the same interfaces implemented by ArrayList, LinkedList and so on. Don't forget to return a type parameter <E>
.
public <E> List<E> sort(List<E> list) {
...
return list;
}
This method is applicable to all of the List implementations as below (I don't remember them all):
List<String> vector = new Vector<>();
List<String> stack = new Stack<>();
List<String> arrayList = new ArrayList<>();
List<String> linkedList = new LinkedList<>();
List<String> newList1 = new Main().sort(vector);
List<String> newList2 = new Main().sort(stack);
List<String> newList3 = new Main().sort(arrayList);
List<String> newList4 = new Main().sort(linkedList);
Your classmate should have told you to start with the correct declaration to begin with:
public <<E extends Comparable<? super E>>> List<E> sort(List<E> list)
because if you want to sort a List
without passing a Comparator
, those elements need to be somehow, well, comparable.
Then, generally, returning a List
vs ArrayList
, the choice should be for List
. But not always. Think of a Set
vs SortedSet
for a minute, returning SortedSet
makes it clear for the caller that the returned Set
is actually sorted; as opposed to returning a Set
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With