【问题标题】:Generics and type inference in JavaJava中的泛型和类型推断
【发布时间】:2012-11-05 21:36:59
【问题描述】:

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

【问题讨论】:

    标签: java generics


    【解决方案1】:

    之所以有效,是因为writeExact 中的T 是方法本身的类型变量。看,它就在 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&lt;Fruit&gt;,它是,第二个参数必须是 Fruit,它是,因为 @ 987654332@ 是Fruit 的子类型。

    隐式分配类型参数的规则在section 15.12.2.7 of the Java Language Specification 中给出。至少,我认为他们是;这不是一本容易阅读的书。

    我猜书中的代码和你上面给出的代码并不完全相同。如果是,那肯定是不正确的,你应该写信给埃克尔先生告诉他。

    【讨论】:

    • 代码一模一样,我可以和你分享我现在正在阅读的pdf :)。该示例中超类型通配符的用途是什么?
    • 我认为整个示例实际上可能完全是垃圾。有界通配符确实有其用途,但此代码并未演示!说真的,请检查本书的勘误表,看看是否涵盖了这一点,如果没有,请写信询问作者。
    • 我相信正确的语法是this.&lt;Apple&gt;writeExact(...)
    【解决方案2】:
    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 是 apple 的子类型)),因为您使用参数化类型声明它 T

    【讨论】:

    • 那又怎样?除非它们必须不相同(在列表和第二个参数中)?
    • T 应该与您在 void 之前在方法签名中声明的泛型类型相同
    • 我的问题是为什么我可以传递给List&lt;Fruit&gt;Apple等类型的方法
    • 您可以传递 List 和 Apple,因为 Apple 是 Apple 的子类型。但不是相反
    • 那么,是不是书上写错了?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-28
    相关资源
    最近更新 更多