Java类型差异,泛型类型的消费者

asp*_*asp 6 java contravariance

我很难理解Java中的方差是如何工作的.

在以下示例中,我定义了一个函数test,这需要Consumer.函数的定义没有逆变,所以我希望它Consumer<Object>不是一个子类型Consumer<Pair<Animal, Animal>>.然而,代码编译,测试接受lambda Variance:::superAction.

我错过了什么?

import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;

import java.util.function.Consumer;

public class Variance {

  public static void main(String[] args) {
    test(Variance::exactMatchAction);
    test(Variance::superAction);
  }

  private static void exactMatchAction(Pair<Animal, Animal> pair) {
    System.out.println(pair.getLeft().getClass().getName());
  }

  private static void superAction(Object obj) {
    System.out.println(obj.getClass().getName());
  }

  private static void test(Consumer<Pair<Animal, Animal>> action) {
    action.accept(ImmutablePair.of(new Animal(), new Animal()));
    action.accept(ImmutablePair.of(new Dog(), new Dog()));
  }

  static class Animal {  }

  static class Dog extends Animal {  }
}
Run Code Online (Sandbox Code Playgroud)

编辑:Per @ Thielo的评论,引用superAction是des a to a Consumer<Pair<Animal, Animal>>a a Consumer<Object>.

给出test方法的正确类型是这样的:

void test(Consumer<? super Pair<? extends Animal, ? extends Animal>>)
Run Code Online (Sandbox Code Playgroud)

这种类型将允许我们传递一个Consumer<Object>to test,并且还允许我们使用诸如Pair<Dog, Dog>而不是仅仅的参数来调用消费者Pair<Animal, Animal>.

作为后续问题,与此更新类型的测试,它不会接受类似的方法参考void exactMatchAction<Pair<Animal, Animal>>了,只void exactMatchAction<Pair<? extends Animal, ? extends Animal>>.为什么是这样?

cre*_*hen 1

方法引用表达式(例如您的Variance::superAction)是多表达式(JLS8,15.13)。多表达式的类型可能会受到表达式的目标类型(JLS8,15.3)的影响,这是在该上下文(JLS8,5)中预期的类型,即,Consumer<Pair<Animal, Animal>>在您的情况下。

详细信息在 JLS8, 15.13.2 中有详细说明。基本思想是对函数接口类型(例如Consumer. 具体来说,方法类型只需要与函数类型一致Pair<Animal, Animal> -> void(注意,Consumer这里已经从类型考虑中消失了),这可以通过“identif[ying]与引用相对应的单个编译时声明”来满足(并具有void返回类型)。这里,“识别”声明的概念可以追溯到15.12.2,基本上描述了方法重载解析过程。换句话说,该语言现在采用Consumer<Pair<Animal, Animal>>.accept()(即Pair<Animal, Animal>) 所需的函数参数,并检查是否可以用该参数调用方法引用(这可以解决重载问题,以防存在多个同名静态方法)。