【问题标题】:Why does Function<Interface, R> not accept a method reference from a subclass?为什么 Function<Interface, R> 不接受来自子类的方法引用?
【发布时间】:2020-05-05 06:03:34
【问题描述】:

假设我有一个接口Animal,方法是String getRace() 和一个子类Dog。我在List&lt;Animal&gt; 里养了一些动物。

现在,如果我想对该列表进行排序并将Function&lt;Animal, String&gt; 传递给方法以将其与Comparator.comparing(...) 一起使用,那将可以正常工作。但是,如果我从 Dog 引用一个函数作为参数传递,编译器会抛出以下错误:

Zoo.Dog 类型没有定义这里适用的 getRace(Zoo.Animal)。

即使它是 Animal 的子类并且确实定义了该函数。

代码如下:

import java.util.*;
import java.util.function.*;
import java.util.stream.*;

public class Zoo {

    public interface Animal {

        public String getRace();

    }

    public class Dog implements Animal {

        public String getRace() {
            return "Some race";
        }

    }

    private List<Animal> animals = Arrays.asList(new Animal[] {});

    public void sort(Function<Animal, String> function) {
        animals = animals.stream().sorted(Comparator.comparing(function)).collect(Collectors.toList());
    }

    public static void main(String[] args) {
        Zoo zoo = new Zoo();

        // Error: "The type Zoo.Dog does not define getRace(Zoo.Animal) that is applicable here"
        zoo.sort(Dog::getRace);
    }

}

这是为什么呢?

我找到了this question,但在该示例中,这些类之间没有任何关系。

【问题讨论】:

  • 因为ListAnimal 类型而不是Dog 类型
  • @Deadpool 是的,我有点期待这样的事情。我很困惑,因为Dog 可以做Animal 可以做的所有事情。
  • 简而言之:不是每个Animal 都是Dog

标签: java function generics


【解决方案1】:

这行得通:

public static void main (String[] args) {
    Zoo zoo = new Zoo();
    zoo.sort(Animal::getRace);
}

原因很简单:

要使用ComperatorAnimal 的列表进行排序,它必须是Comperator&lt;Animal&gt;。这意味着您传递给Comperator.comparing 的函数需要 属于Function&lt;? super Animal,? extends Comperable&gt;。这不是 Function&lt;Dog, String&gt; 为真,但对于 Function&lt;Animal, String&gt;Function&lt;Object, String&gt;

您正确地定义了您的.sort() 以采用Function&lt;Animal, String&gt; - 但是您传入了Dog 的成员函数- 这在功能接口中本质上是一个函数Dog -&gt; StringFunction&lt;Dog,String&gt;


用较少的技术术语来说:是的,Dog 可以做任何事情,Animal 可以。然而,这种方法并不正确——Dog 可以具有并非所有动物都具有的功能。因此,使用 Function&lt;Dog,?&gt; 代替 Function&lt;Animal,?&gt; 是无效的 - 但这样也可以。

【讨论】:

  • 对,有道理。我以前没有真正使用过方法引用,这就是我感到困惑的原因。
  • @Shred 更多的是泛型问题。 Java 中的泛型类型,包括泛型接口,仅由编译器强制执行。所有类型信息在运行时都会被删除——如果你想用谷歌搜索,这称为“类型擦除”。这意味着您只能使用编译可以确保的泛型 - 这就是为什么您不能在此处放置 Dog 的函数来代替 Animal 的函数。编译器无法确保在运行时方法签名仍然与Animal 之一兼容。
【解决方案2】:

Dog::getRace 需要 Dog 实例,但 animals.stream() 提供 Animal 实例。

我猜应该怎么做,因为Dog 实现了Animal 并且getRaceAnimal 中声明:

    zoo.sort(Animal::getRace);

【讨论】:

    猜你喜欢
    • 2021-04-29
    • 2020-07-28
    • 2022-06-23
    • 2011-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-28
    相关资源
    最近更新 更多