Reputation: 41
I have these two methods under a generic class:
public class Container<S> {
public void f2(List<Object> l1, List<?> l2) {
l1 = l2; //compilation error row #1
}
public void f3(List<?> c, List<S> l) {
c = l; //ok row #2
l = c; //compilation error row #3
}
}
I really don't undersatand why row 2 is ok- if I transfer this method two lists, one is an object type list and one is a Strings one, I shoule get a compilation error?
would really appriciate to understadn why each row should/shouldn't be compiled.
Upvotes: 4
Views: 92
Reputation: 89
Generics and wildcards are powerful because they ensure better typechecking during compile time, that's the main purpose of using them. To make sure your code won't break at runtime due to poor type checking.
Although Integer is a subtype of Number,
List<Integer>
is not a subtype ofList<Number>
and, in fact, these two types are not related. The common parent ofList<Number>
andList<Integer>
isList<?>
.In order to create a relationship between these classes so that the code can access
Number
's methods throughList<Integer>
's elements, use an upper bounded wildcard:List<? extends Integer> intList = new ArrayList<>(); List<? extends Number> numList = intList; // OK. List<? extends Integer> is a subtype of List<? extends Number>
Because
Integer
is a subtype ofNumber
, andnumList
is a list ofNumber
objects, a relationship now exists betweenintList
(a list ofInteger
objects) andnumList
. The following diagram shows the relationships between several List classes declared with both upper and lower bounded wildcards.
To make use of subtyping and polymophism with wildcards you have to use bounded wildcards.
public void f2(List<Object> l1, List<?> l2) {
l1 = l2; //compilation error row #1
}
//correct way of doing it
public void f2(List<? extends Object> l3, List<?> l4) {
l3 = l4;
}
Compiler doesn't see l2 as a subtype of l1
public void f3(List<?> c, List<S> l) {
c = l; //ok row #2
l = c; //compilation error row #3
}
Compiler doesn't see c as a subtype of l (rightly so, this could lead to runtime errors).
Upvotes: 1
Reputation: 424993
Why does this compile?
List<?> c, List<S> l;
c = l; // OK
List<?>
means (more or less) “list of something” (or more formally “list of unknown”), and a List<S>
is one of those.
——
Why does this not compile?
List<Object> l1, List<?> l2;
l1 = l2; //compilation error
If it were allowed, you could then add anything (eg String) to l1
, but if l2
is anything other than List<Object>
(eg Integer), you’d be putting the wrong type in l2
. That’s why this assignment is not allowed.
The other compilation error is more subtle, and also doesn’t have a use case - that is, there’s no reason to assign a typed list to an untyped one - but the wildcard ?
really means “unknown, but specific”. This is not the same as “anything”. It’s “something”, but we don’t know what. Type S
is something, but the compiler can’t verify that it’s the same something as S
.
Upvotes: 3