下面我有两种通用方法。两者都有两个参数化类型T和V扩展T.第一个方法foo接受类型为T和V的参数。奇怪的是,我可以调用foo(“hello”,new Integer(10))并编译并运行即使整数显然不会扩展String。第二种方法bar采用List<类型的参数。 T>和列表< V>。在这里,我不能调用bar,传递一个字符串列表和一个整数列表。为什么后者限制类型,前者不限制。
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
}
}
答案 0 :(得分:1)
foo
将始终能够获取任何引用类型的两个参数,因为T
和V
可以选择为Object
,并且任何引用类型都是Object
的子类型。因此,foo
也可以声明为public static void foo(Object obj1, Object obj2)
;它没有任何区别。声明它的方式,foo
除了可以使用Object
完成的事情外,没有任何Object
可以对其参数做任何事情,因为类型变量没有限制,所以没有理由任何超出限制的限制都是bar
。
List<String>
不同,因为type变量在type参数中。泛型是不变的 - List<Object>
不是String
的子类型(反之亦然),即使Object
是List<T>
的子类型。因此,通过使第一个参数为T
类型,它会强制List<String>
与传递的参数的类型参数完全匹配;即通过传递T
作为第一个参数,它强制String
为Object
;它不能是List<V>
或其他任何东西 - 它不会兼容。由于第二个参数的类型为V
且T
扩展String
,因此第二个参数的类型参数必须为String
或子类型为{{1 }}。 (p.s. bar
也可以宣称为public static <T> void bar(List<T> list1, List<? extends T> list2)
)