为啥接口的泛型方法可以在 Java 中实现为非泛型?

Posted

技术标签:

【中文标题】为啥接口的泛型方法可以在 Java 中实现为非泛型?【英文标题】:Why a generic method of an interface can be implemented as non-generic in Java?为什么接口的泛型方法可以在 Java 中实现为非泛型? 【发布时间】:2016-08-10 02:49:11 【问题描述】:

假设我们有几个这样的测试接口/类:

abstract class Plant 
    public abstract String getName();


interface Eatable  

class Apple extends Plant implements Eatable 
    @Override
    public String getName() 
        return "Apple";
    


class Rose extends Plant 
    @Override
    public String getName() 
        return "Rose";
    


interface Animal 
    <T extends Plant & Eatable> void eat(T plant);

您可以看到Animal.eat 是一个带有约束的泛型方法。现在我的Human 类是这样的:

class Human implements Animal 
    @Override
    public void eat(Plant plant) 
    

编译得很好。你可以看到Human.eat 的约束比Animal.eat 少,因为Eatable 接口丢失了。

Q1:为什么编译器不抱怨这种不一致?

Q2:如果编译器可以接受Plant&amp;Eatable降级Plant,为什么它会抱怨eat(Object plant)

【问题讨论】:

您使用的是哪个版本的 Java。在 Eclipse 中使用 Java 1.8 会产生预期的编译时错误。 @Codebender Java8 in Intellij IDEA。 @Codebender 我编译这个没有问题。 ideone.com/7xUcZn @ElliottFrisch 但它发生在编译阶段,编译器知道这一点,我认为它应该给出一个错误,至少是一个警告? 这正是@ElliottFrisch 刚才所说的。 【参考方案1】:

Lesson: Generics by Gilad Bracha 据他说

public static <T extends Object & Comparable<? super T>> T max(Collection<T> coll)

这是为类型参数提供多个边界的示例, 使用语法 T1 & T2 ... & Tn。具有多个的类型变量 bounds 是已知的所有类型的子类型 边界。当使用多重边界时,第一种类型在 bound 用作类型变量的擦除。

因此您的示例 &lt;T extends Plant &amp; Eatable&gt; void eat(T plant); 将被删除为 void eat(Plant plant);,因此当您覆盖它时,编译器不会抱怨

【讨论】:

【参考方案2】:

艾哈迈德的回答是对的,顺便说一下,如果你想约束Animal接口的实现,你应该这样声明:

interface Animal<T extends Plant & Eatable>  
    void eat(T plant);

然后,如果您在不提供类型信息的情况下实现 Animal 接口,编译器将使用最少意外策略将 T 推断为 Plant 类型。但是,如果您提供必要的类型信息,编译器就可以正常工作。

class Human implements Animal<Rose> // won't compile
class Human implements Animal<Apple> // compile

【讨论】:

以上是关于为啥接口的泛型方法可以在 Java 中实现为非泛型?的主要内容,如果未能解决你的问题,请参考以下文章

C# 中的泛型与非泛型性能

返回类型为协议的泛型函数与参数和返回类型为协议的非泛型函数的区别

java泛型接口是怎么一回事,干啥用的

为啥在存在非泛型时选择泛型方法?

Java 泛型

非泛型集合