【问题标题】:How to use generic type parameter in a method of an interface如何在接口的方法中使用泛型类型参数
【发布时间】:2020-04-19 23:01:04
【问题描述】:

我创建了一个界面:

public interface Person {
    <T extends Food> void eat(T food);
}

其中Food 是具体但非最终的class

我想用Food的子类实现一个Person的eat方法,即Fruit,像这样:

public class Man implements Person {
    @Override
    public void eat(Fruit food) {
        //implementation here
    }
}

但是有编译错误

问题:为什么会出现编译错误?如何在不向public interface Person&lt;T&gt;之类的接口添加泛型类型的情况下修复错误?

【问题讨论】:

    标签: java generics


    【解决方案1】:

    你不能用泛型来做到这一点。

    假设您编写的代码已编译。那么这样做就没有意义了

    Person person = new Man();
    person.eat(new Steak()); //where Steak is a subclass of Food
    

    现在应该调用什么方法? Person 仅定义了一个方法 eat,它具有 Fruit 参数。

    泛型提供编译时类型安全,因此它们在编译期间被显式显示。这称为擦除过程。编译器将删除所有类型参数并在必要时添加一些类型转换。

    这个方法定义在Person

    <T extends Food> void eat(T food);
    

    编译后会变成

    void eat(Food food);
    

    因此,如果您像以前那样实现 Man,那么 Person 中将有两个名为 eat 的方法,它们具有不同的参数,因此它们是不同的方法。

    void eat(Food food);
    void eat(Fruit food);
    

    一种方法是像这样将泛型移动到类级别

    public interface Person<T extends Food> {
        void eat(T food);
    }
    
    //We enforce the Man class to only "eat" Fruit foods.
    public class Man implements Person<Fruit> {
        public void eat(Fruit fruit) {
            //Eat some fruit
        }
    }
    
    //This will work and compile just fine
    Person<Fruit> person = new Man();
    Fruit fruit = new Fruit();
    person.eat(fruit);
    

    但这不会编译。

    Person<Fruit> person = new Man();
    Food food = new Fruit();
    person.eat(food); //food must be casted to Fruit
    

    那是因为当您声明 person 时,&lt;Fruit&gt; 会指示编译器检查您是否有效地传递了 Fruit 实例。它在编译时知道您传递的是超类型Food 而不是Fruit,如果代码在此状态下编译,这可能会导致运行时出现强制转换异常,从而破坏泛型所涉及的类型安全。其实

    public class Man implements Person<Fruit> {
        public void eat(Fruit fruit) {
             System.out.println(fruit.toString());
        }
    }
    

    会这样编译

    public class Man implements Person {
        public void eat(Food fruit) {
             //The compiler will automatically add the type cast!
             System.out.println(((Fruit) fruit).toString());
        }
    }
    

    绕过编译器类型安全检查的一种方法是省略这样的泛型

    //This will compile with a warning
    //Unchecked use of 'eat(T)' method
    ((Person)person).eat(food); 
    

    【讨论】:

    • 第一行,你的意思是Person person = new Man();吗?那么person只能吃水果不是吗?
    • 查看新版本
    • 也检查这个类似的问题stackoverflow.com/questions/5346847/…
    • 非常感谢您的解释。我还尝试通过向接口添加泛型类型interface Person&lt;T extends Car&gt;,然后它可以工作。为什么会这样?
    • 我编辑了我的答案,删除了超出问题上下文的关于重载的部分,并添加了关于类级别通用用法的部分
    【解决方案2】:

    Person 接口中 eat 方法的约定指定该方法适用于作为 Food 子类型的任何类型。当你在 Person 类中定义另一个只接受 Food 实例的方法时,你没有遵守接口中方法的约定,因此你没有覆盖它。

    正如您所提到的,要在使用泛型的类的实现中设置具体类型,类本身必须是泛型的(您必须在类级别具有泛型类型)。

    【讨论】:

    • 非常感谢您的回答。您的最后一句话:要在使用泛型的类的实现中设置具体类型,类本身必须是泛型的。这听起来很合逻辑,我对其进行了测试以确认其有效性。但我实际上无法理解为什么类本身必须是通用的逻辑
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多