我很难理解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 { }
}
编辑:Per @ Thielo的评论,参考superAction
被Consumer<Pair<Animal, Animal>>
而不是Consumer<Object>
。
提供test
方法的正确类型如下:
void test(Consumer<? super Pair<? extends Animal, ? extends Animal>>)
此类型允许我们将Consumer<Object>
传递给test
,并且还允许我们使用Pair<Dog, Dog>
之类的参数而不仅仅Pair<Animal, Animal>
来调用消费者。
作为后续问题,使用此更新的测试类型,它将不再接受void exactMatchAction<Pair<Animal, Animal>>
之类的方法引用,仅接受void exactMatchAction<Pair<? extends Animal, ? extends Animal>>
。这是为什么?
答案 0 :(得分:0)
方法引用表达式(例如Variance::superAction
)是多重表达式(JLS8,15.13)。 poly表达式的类型可能受表达式目标类型(JLS8,15.3)的影响,这是该上下文中预期的类型(JLS8,5),即{{1在你的情况下。
细节在JLS8,15.13.2中详细说明。基本思想是对Consumer<Pair<Animal, Animal>>
等功能接口类型进行特殊处理。具体来说,方法类型只需 congruent 到函数类型(Consumer
- 注意Pair<Animal, Animal> -> void
已经从类型考虑中消失了),这是由满足&#34;识别[ying]与参考&#34;相对应的单个编译时声明。 (并且Consumer
作为返回类型)。在这里,&#34;识别&#34;的概念声明可以追溯到15.12.2,基本上描述了方法重载解析过程。换句话说,该语言现在采用void
所期望的函数参数(即Consumer<Pair<Animal, Animal>>.accept()
)并检查是否可以使用该函数调用方法引用(如果存在多个静态方法,则解决了重载问题)同名)。