I am running into a very strange issue when applying a method reference to the second type in an intersection type. Here is a minimal test case:
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());
}
}
This code compiles but fails in run
at runtime with error:
java.lang.invoke.LambdaConversionException: Invalid receiver type interface LambdaTest$A; not a subtype of implementation type interface LambdaTest$B
This issue even occurs when explicitly specifying the type of the lambda:
static <E extends A & B> void run(E e) {
final Function<E, B> doIt = B::doIt;
Collections.singleton(e).stream().map(doIt);
}
Apparently the lambda is expected to take specifically the first type in the intersection and not the second. Sure enough, changing the method signature of run
as follows makes the error go away:
static <E extends B & A> void run(E e) {
Interestingly, a couple other minor changes also fix the error. Calling a method in Object works, presumably because any A must also have that method:
Collections.singleton(e).stream().map(B::toString);
Also, pulling the function reference out of run
fixes the error:
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);
}
Finally, converting the method reference to a regular lambda fixes the issue:
Collections.singleton(e).stream().map(x -> x.doIt());
So it seems that there is an issue specifically with method references. The compiler allows any method reference that is valid on the intersection type. However, the implementation assumes that the method reference will take the first type of the intersection, A
, even if the type of the lambda is explicitly specified. The implementation then checks whether the method is available on type A
and if not, fails.
Is my analysis correct? Is this behavior intentional or is it a bug?