Here is a simplified example which reproduces the problem and uses only core Java classes:
public static void main(String[] argv) {
System.out.println(dummy("foo"));
}
static <T extends Serializable&CharSequence> int dummy(T value) {
return Optional.ofNullable(value).map(CharSequence::length).orElse(0);
}
Your assumption is correct, the JRE-specific implementation receives the target method as a MethodHandle
which has no information about generic types. Therefore the only thing it sees is that the raw types mismatch.
Like with a lot of generic constructs, there is a type cast required on the byte code level which doesn’t appear in the source code. Since LambdaMetafactory
explicitly requires a direct method handle, a method reference which encapsulates such a type cast cannot be passed as a MethodHandle
to the factory.
There are two possible ways to deal with it.
First solution would be to change the LambdaMetafactory
to trust the MethodHandle
if the receiver type is an interface
and insert the required type cast by itself in the generated lambda class instead of rejecting it. After all, it does similar for parameter and return types already.
Alternatively, the compiler would be in charge to create a synthetic helper method encapsulating the type cast and method call, just like if you had written a lambda expression. This is not a unique situation. If you use a method reference to a varargs method or an array creation like, e.g. String[]::new
, they can’t be expressed as direct method handles and end up in synthetic helper methods.
In either case, we can consider the current behavior a bug. But obviously, compiler and JRE developers must agree on which way it should be handled before we can say on which side the bug resides.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…