Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to define Java function's return type and parameters, using subclass or superclass?

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) {...}
like image 840
R Ree Avatar asked Feb 18 '18 19:02

R Ree


3 Answers

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.

like image 169
Itay Maman Avatar answered Oct 07 '22 03:10

Itay Maman


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);
like image 1
Nikolas Charalambidis Avatar answered Oct 07 '22 01:10

Nikolas Charalambidis


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.

like image 1
Eugene Avatar answered Oct 07 '22 03:10

Eugene