Reputation: 3653
Below I have two generic methods. Both have two parameterized types T and V extends T. The first method, foo, takes arguments of type T and V. Strangely, I can call foo("hello", new Integer(10)) and it compiles and runs even though Integer clearly doesn't extend String. The second method, bar, takes arguments of type List< T > and List< V >. Here, I can not call bar, passing it a List of Strings and a List of Integers. Why does the latter restrict the type, and the former not.
public class GenMeth {
public static void main(String[] args) {
List<String> s_list = new ArrayList<>();
s_list.add("hello");
List<Integer> i_list = new ArrayList<>();
i_list.add(10);
foo("hello", new Integer(10)); // will compile - why?
bar(s_list, i_list); // won't compile - understandable
}
public static <T,V extends T> void foo(T obj1, V obj2) {
// do something
}
public static <T,V extends T> void bar(List<T> list1, List<V> list2) {
// do something
}
}
Upvotes: 0
Views: 207
Reputation: 122489
foo
will always be able to take two arguments of any reference type, because T
and V
can be chosen to be Object
, and any reference type is a subtype of Object
. So foo
might as well be declared as public static void foo(Object obj1, Object obj2)
; it would make no difference. The way it is declared, there is nothing that foo
can do with either of its parameters except the things that can be done with Object
, since the type variables are not bounded, so there is no reason for any restriction beyond that they are both Object
.
bar
is different because the type variable is in the type parameter. Generics are invariant -- List<String>
is not a subtype of List<Object>
(nor vice versa) even though String
is a subtype of Object
. So by having the first parameter be type List<T>
, it forces T
to match the type parameter of the passed argument exactly; i.e. by passing a List<String>
as the first argument, it forces T
to be String
; it can't be Object
or anything else -- it wouldn't be compatible. And since the type of the second parameter is List<V>
and V
extends T
, it means the type parameter of the second argument must be String
or a subtype of String
. (p.s. bar
can also be declared a little simpler as public static <T> void bar(List<T> list1, List<? extends T> list2)
)
Upvotes: 1