我正在阅读“Thinking in Java 4th edition”中的泛型章节,并且有一个例子:
class Fruit{}
class Apple extends Fruit {}
...
static <T> void writeExact(List<T> list, T item) {
list.add(item);
}
static List<Apple> apples = new ArrayList<>();
static List<Fruit> fruit = new ArrayList<>();
static void f1() {
writeExact(apples, new Apple());
// writeExact(fruit, new Apple()); // Error:
// Incompatible types: found Fruit, required Apple
}
static <T> void writeWithWildcard(List<? super T> list, T item) {
list.add(item);
}
static void f2() {
writeWithWildcard(apples, new Apple());
writeWithWildcard(fruit, new Apple());
}
指示错误的那些注释行在6和7 Java中都不会产生任何错误。这对我来说似乎很奇怪,因为writeExact
方法只接受精确类型的参数。那它为什么有效呢?因为它的作用是超级通配符的目的吗?
修改
所以我明白了,并想澄清一件事: 谁是带有通配符的类型推断的老大?返回类型,第一个参数,第二个参数,....我的意思是如果没有指定SomeClass。&lt; Fruit &gt;方法(...)
答案 0 :(得分:2)
它有效,因为T
中的writeExact
是方法本身的类型变量。看,它就在void返回类型之前就已经存在了:
static <T> void writeExact(List<T> list, T item)
这意味着无论何时调用它,它的T
都会采用接受其参数所需的类型,只要有一个。您可以使用任何列表和适合该列表的对象来调用它。
也许它更明确地给出了类型参数,而不是让编译器推断它(你需要使用类的名称来为静态方法执行此操作;我将假设它是MyClass
):
MyClass.<Apple>writeExact(apples, new Apple());
MyClass.<Fruit>writeExact(fruit, new Apple());
在第二个调用中,T
的值为Fruit
,这意味着第一个参数必须是List<Fruit>
,而第二个参数必须是Fruit
},它是,因为Apple
是Fruit
的子类型。
隐式分配类型参数的规则在section 15.12.2.7 of the Java Language Specification中给出。至少,我认为他们是;这不是一个简单的阅读。
我猜本书中的代码与您上面提供的代码并不完全相同。如果是,那么它肯定是不正确的,你应该写信给Eckel先生告诉他。
答案 1 :(得分:1)
static <T> void writeExact(List<T> list, T item) {
list.add(item);
}
会接受(应用程序列表,Apple或水果列表,水果或Applles列表,Fruit(因为Fruit是苹果的子类型)),因为您使用参数化类型 T 声明它。 / p>