3

我正在阅读“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.< Fruit >method(...) 没有指定

4

2 回答 2

2

它之所以有效,是因为TinwriteExact是方法本身的类型变量。看,它就在 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,这意味着第一个参数必须是 a List<Fruit>,它是,第二个参数必须是 a Fruit,它是,因为Apple是 的子类型Fruit

Java 语言规范的第 15.12.2.7 节给出了隐式分配类型参数的规则。至少,我认为他们是;这不是一本容易阅读的书。

我猜这本书中的代码与你上面给出的代码并不完全相同。如果是,那么它肯定是不正确的,你应该写信给 Eckel 先生并告诉他。

于 2012-11-04T21:40:54.050 回答
1
static <T> void writeExact(List<T> list, T item) {
            list.add(item);
    }

会接受 (List of Applies,Apple or List of Fruits, Fruit or List of Applles, Fruit(as Fruit is the subtype of apple)) 因为您使用参数化类型T声明了它。

于 2012-11-04T21:40:10.023 回答