我正在尝试学习java泛型。我写了一个方法:
public <T> T Gmethod(T a,Collection<T> list){
return a;}
使用Gmethod("A",list);
调用此功能时,列表的类型为Object
,
但是使用Gmethod(new Object(),list);
调用它,其中list是String
的类型是错误,
为什么呢?
答案 0 :(得分:4)
具有
<T> T method(T a, Collection<T> list) { return a; }
并用
调用它Collection<Object> list = new LinkedList<>();
method("A", list);
将导致编译器推断类型变量Object
的类型T
,因为list
的类型为Collection<Object>
。变量a
的类型是String
,它是Object
的子类型。所以允许这个方法调用。
使用
调用方法Collection<String> list = new LinkedList<>();
method(new Object(), list);
将再次使编译器推断类型变量T
的类型Object,因为现在a
的类型为Object
。因此,编译器请求参数list
的参数为Collection<Object>
类型(或子类型)。 但Collection<String>
不是Collection<Object>
的子类型。因此编译器不允许此方法调用。
答案 1 :(得分:2)
当你有一个引用T
时,实际的类可以是T或子类。当你有一个<T>
时,它必须是那个类而且只有那个类。这是必需的,因为它是引用的容器,修改此容器可以更改原始容器。普通引用不会发生这种情况,因为它是作为副本传递的。
引用按值复制。
String a = "hi";
Object b = a;
b = new Object(); // no problem as `a` is not changed by this.
但是对于容器来说,这种方法不能很好地工作,因为只复制对容器的引用,而不是容器本身。
List<String> a = new ArrayList<>();
a.add("hi");
List<Object> b = (List) a; // compiler warning.
b.add(new Object()); // alters `a` in an incorrect way.
a.get(1); // ClassCastException oops.
答案 2 :(得分:1)
正如@Seelenvirtuose和@Peter Lawrey在答案中建议的那样,这是因为编译器为你做的类型推断。
要测试编译器将第一次调用绑定到Object
,请尝试使用
class Main{
public static void main(String[] args){
Collection<Object> listOfObject = new ArrayList<>();
Main.<String>method("a",listOfObject); //compile time error here
}
public static <T> T Gmethod(T a, Collection<T> coll){
return a;
}
}