在交集类型中将方法引用应用于 second 类型时,我遇到了一个非常奇怪的问题。这是一个最小的测试用例:
class LambdaTest {
interface A {}
interface B {
B doIt();
}
static class AB implements A, B {
public B doIt() {
return this;
}
}
static <E extends A & B> void run(E e) {
Collections.singleton(e).stream().map(B::doIt);
}
public static void main(String[] args) {
run(new AB());
}
}
此代码在运行时编译但在run
中失败但错误:
java.lang.invoke.LambdaConversionException:无效的接收器类型接口LambdaTest $ A;不是实现类型接口LambdaTest $ B
的子类型
当明确指定lambda的类型时,甚至会出现此问题:
static <E extends A & B> void run(E e) {
final Function<E, B> doIt = B::doIt;
Collections.singleton(e).stream().map(doIt);
}
显然,lambda预计将特别采用交叉口中的第一种类型而不是第二种类型。果然,如下更改run
的方法签名会使错误消失:
static <E extends B & A> void run(E e) {
有趣的是,一些其他的小改动也可以解决错误。调用Object中的方法是有效的,大概是因为任何A也必须有这个方法:
Collections.singleton(e).stream().map(B::toString);
此外,从run
中提取函数引用可修复错误:
static <E extends A & B> void run(E e, Function<B, ?> f) {
Collections.singleton(e).stream().map(f);
}
public static void main(String[] args) {
run(new AB(), B::doIt);
}
最后,将方法引用转换为常规lambda可以解决问题:
Collections.singleton(e).stream().map(x -> x.doIt());
所以似乎特别是方法引用存在问题。编译器允许任何对交集类型有效的方法引用。但是,实现假定方法引用将采用第一种类型的交集A
,即使明确指定了lambda的类型。然后,实现检查该方法是否在类型A
上可用,如果不可用,则失败。
我的分析是否正确?这种行为是故意还是错误?