views:

625

answers:

8

The question below is from Java SCJP5 book by Kathy Sierra and Bert Bates. Given a method declared as:

public static <E extends Number> List<E> process(List<E> nums)

A programmer wants to use the method like this:

// INSERT DECLARATIONS HERE
output = process(input);

Which pair of declarations could be placed at // INSERT DECLARATIONS HERE to allow the code to compile? (Choose all that apply.)

A.

ArrayList<Integer> input = null;
ArrayList<Integer> output = null;

B.

ArrayList<Integer> input = null;
List<Integer> output = null;

C.

ArrayList<Integer> input = null;
List<Number> output = null;

D.

List<Number> input = null;
ArrayList<Integer> output = null;

E.

List<Number> input = null;
List<Number> output = null;

F.

List<Integer> input = null;
List<Integer> output = null;

G. None of the above.

Correct Answers given are: B, E, F and the explanation in the book states:
"The return type is definitely declared as List, NOT ArrayList so A,D are wrong. ......"

This is what I don't get...why it is that the return type MUST be List only and not ArrayList?? Just like the argument can be ArrayList then why cant return type also be arrayList?

Thanks

+12  A: 

Because ArrayList is a subclass of List, so the List returned by process is not guaranteed to be an ArrayList. For example, it could be a LinkedList instead.

starblue
+3  A: 

When you specify a return of type List, you're saying that the return can be any kind of List, whether it be an ArrayList, LinkedList, or other kind of List. If you try to return an ArrayList, you're being too specific - it's no longer a generic List.

Tim
basically what I felt like saying. Am not quite sure whether I formulated my thoughts correctly (clearly enough)
Peter Perháč
We said effectively the same thing. You make a good point about process not being able to choose the concrete implementation of List - I like that phrasing.
Tim
A: 

It is because the question explicitly says the method returns List<T>. You may very well change the method definition to return ArrayList<T>, but that's a different question. A List<T> can be many different types of list.

krosenvold
+1  A: 

The process method will not be in a position to decide which concrete implementation of List to choose. Its signature gives you a promise that it will be able to operate on ANY List (ArrayList, LinkedList, etc...) and can only specify that the return value will be SOME List.

For example, if the returned object is a LinkedList you couldn't assign it directly to a variable declared as ArrayList, but you could treat it as a List. Therefore A. and D. are not correct. You can't have your variable declared as ArrayList.

Peter Perháč
+2  A: 

This is actually not specific to generics, but deals with types.

Easy way to think of it is, an ArrayList is a List, but an List is not necessarily an ArrayList.

ArrayList implements the List interface, so it can be treated as a List. However, just because something implements List, it is not an ArrayList. For example, LinkedList implements List, but is not an ArrayList.

For example the following are allowed:

List arrayList = new ArrayList();
List linkedList = new LinkedList();

This is because both ArrayList and LinkedList both implement the List interface, so they both can be handled as Lists.

However, the following is not allowed:

ArrayList arrayList = new LinkedList();

Although both ArrayList and LinkedList implement List, they are not the same class. They may have similarities by implementing the methods of List, but they are completely separate classes.

coobird
Not strictly true - this is akin to the old "square is a rectangle, but a rectangle's not a square" argument. A List *can be* an ArrayList, but it is not *guaranteed* to be an ArrayList. There's a small but important difference.
Tim
Ah, thanks Tim, I'll fix the wording. :)
coobird
+3  A: 

The return type could be an ArrayList, but it could also not be an ArrayList, it could be a LinkedList, or some Collections List wrapper or other things. In order to assign it to a variable either you have to use the highest level type it could be (List in this case) or downcast if you know it must be an ArrayList.

In practice, variables should almost always be typed as List (if not Collection). There is almost never a good reason to actually reference the concrete type for these classes.

The only reason I can think of is if you have a method that will require random access to a List and you want to ensure you are not getting a LinkedList for performance reasons and the List is too large to reasonably copy to an ArrayList for the purpose of random access.

Yishai
+1  A: 

You can declare return type to be ArrayList, but it would require an explicit cast, like so:

ArrayList output = null;
output = (ArrayList)process(input);

...which is contrary to what generics allow you to accomplish.

Cuga
and you would have to rely on the actual object returned to be `castable` to ArrayList (An ArrayList or a derivate of it)
Peter Perháč
That's right-- you would get a runtime exception if you mistakenly cast to the wrong type of object, whereas generics will tell you at compile time whether the variable assignment is valid or not. Plus I believe this would result in an annoying warning in your IDE about the unchecked cast.
Cuga
A: 

Another way to look at this is "assignment compatibility". Semantically, the method call

output = process(input)

is equivalent to

nums = input;
/* body of process... */
output = returnVal; /* where process exits with "return returnVal" */

We know that nums has type List<E>, so the type of input must be "assignable" to List<E>. Likewise, we know that returnVal (i.e, the return value of process) type List<E>, so List<E> must be "assignable" to the type of output.

In general, a type T is "assignable" to a type U if T is a subtype of U (including the case where T and U are the same).

Hence, the type of input must be List<E> or a subtype of List<E> (e.g., ArrayList<E> or LinkedList<E>). Similarly, the type of output must be List<E> or a supertype of List<E> (e.g., Collection<E> or even Object).

Chris Conway