Java中的泛型和类型推断

时间:2012-11-04 21:34:44

标签: java generics

我正在阅读“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;方法(...)

2 个答案:

答案 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 },它是,因为AppleFruit的子类型。

隐式分配类型参数的规则在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>