tags:

views:

187

answers:

6

I must have missed something regarding generics in Java, but why does this not work?

List<String> list = new ArrayList<String>();

cannot be sent to:

method( List<Object> );

But it does not work? How come?

But if the method was:

method( Object o )

it can be used with:

method( new String("hello") )

without any problems

q1) String does extend Object, why cannot it be passed to?

List<Object>

q2) and why does

method( List<? extends Object> )

work? What's the difference?

+3  A: 

Generics is complex, you may want to read this. I don't thing anything I explain here will be sufficient. Anyway, the short answer is: If List of String is assignable to List of Object, than using the List of Object as a reference, one can add something that is not String to it thus invalidating the "List of String" definition. But it is possible to assign List of String to List of unknown, as a list of unknown can only be read, not added into. Btw, List of ? is the same thing as List of ? extends Object

RichN
+9  A: 

Watch this:

List<String> stringList = new ArrayList<String>();
List<Object> objectList = stringList; // if this was allowed…
objectList.add(new Object());
String s = stringList.get(0); // …this would throw a ClassCastException

Obviously, this can not be allowed to work as the List<String> would contain an Object afterwards, throwing away all the type-safety that generics are aimed to give you.

Bombe
You may want to add a fourth line to the example to make it explicit how the compile-time safety of generic is being violated:`String s = stringList.get(0); // Throws ClassCastException at runtime`
Andrzej Doyle
Good idea. Edited.
Bombe
Brilliant explanation, very to the point.
Buhb
+5  A: 

What if method() were defined like this?

void method(List<Object> list) {
  list.add(new Long(1));
}

Nothing wrong with that right? oops, unless you passed in a List<String>!

It's kind of hard to explain the intuition, but I can point out that the get() methods on a List would present no such problem, right? Since list only promises to return an Object. It's the "set" or "add" methods that are the issue. There it needs to match on supertype, not subtype. The restriction kind of goes both ways, in general, in generics-land. So it's different than simple parameter passing, where a subclass is always OK.

Sean Owen
+2  A: 

The problem you are facing here is denominated Covariance.

Basically, if a Giraffe is an Animal why shouldn't a List<Giraffe> be a List<Animal>? This makes total sense but it can cause problems like Sean mentioned.

Your second question as to do with the way Java addresses this problem. Bounded Wildcards allow you to define covariant(and contravariant) Lists that are safe from the problems mentioned. For instance, you can not add an element to a Bounded Wildcard List.

bruno conde
+4  A: 

List<String> is not a subtype of List<Object>, as you might expect, for reasons mentioned in the other answers. However, you can define your method like this:

method(List<? extends String> list)

which will allow you to get Strings from the list, but not to put anything in. You could pass a List<String> or, theoretically, a List of any subtype of String into the method. You can also define it as

method(List<? super String> list)

which will allow you to put Strings in the list, but only read Objects from it. You can then pass in a List<String> or a List<Object>. Note that it doesn't make much sense in this example (since you can't subclass String), but it does make sense for other type hierarchies

Jorn
A: 

Is that the same reason why this does not work?

private void hello(List<? extends Object> l) {
 l.add( new String(""));
}
corgrath