LambdaConversionException with generics: JVM bug?

Steve McKay :

I have some code with a method reference that compiles fine and fails at runtime.

The exception is this:

Caused by: java.lang.invoke.LambdaConversionException: Invalid receiver type class redacted.BasicEntity; not a subtype of implementation type interface redacted.HasImagesEntity
    at java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:233)
    at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:303)
    at java.lang.invoke.CallSite.makeSite(CallSite.java:289)

The class triggering the exception:

class ImageController<E extends BasicEntity & HasImagesEntity> {
    void doTheThing(E entity) {
        Set<String> filenames = entity.getImages().keySet().stream()
            .map(entity::filename)
            .collect(Collectors.toSet());
    }
}

The exception is thrown trying to resolve entity::filename. filename() is declared in HasImagesEntity. As far as I can tell, I get the exception because the erasure of E is BasicEntity and the JVM doesn't (can't?) consider other bounds on E.

When I rewrite the method reference as a trivial lambda, everything is fine. It seems really fishy to me that one construct works as expected and its semantic equivalent blows up.

Could this possibly be in the spec? I'm trying very hard to find a way for this not to be a problem in the compiler or runtime, and haven't come up with anything.

Holger :

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.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

Java Casting: Java 11 throws LambdaConversionException while 1.8 does not

Java Bounded Generics: Type inference bug? (Method invocation, JLS 15.12.2.7)

LambdaConversionException when mixing method reference and generics

BootstrapMethodError caused by LambdaConversionException caused by using MethodHandle::invokeExact as a method reference

Is this a JVM bug or "expected behavior"?

Default method returns true for a while, and then returns false? (Possible JVM bug)

Is it possible for Scala to have reified generics without changing the JVM?

Java generics bug?

Java generics of generics of

JVM consumes all CPU, most threads as BLOCKED. JVM bug?

Will the JVM be extended to handle generics?

Java generics ECLIPSE bug?

Are there generics with type parameters (generics with generics)?

NullPointerException instead of null (JVM Bug?)

Generics of generics in Swift 3

Swift 3.1 Nested Generics Bug with Cyclic Metadata

JVM bug? java.lang.VerifyError: Bad type on operand stack

Is there a bug with the TypeScript compiler regarding generics, or am I missing something?

Elements of int[] can be inappropriately set to zero in an implemented method (JVM Bug)

Using IntStream leads to elements of int[] to be inappropriately set to zero in an implemented method (JVM Bug)

Using IntStream leads to some array elements to be inappropriately set to zero (JVM Bug, Java 11)

Why is the size of BitSet negative when using Integer.MAX_VALUE (JVM BUG)?

Unexpected result when subtracting in a loop (known and fixed JVM bug)

IntStream leads to array elements being wrongly set to 0 (JVM Bug, Java 11)

Why is the size of BitSet negative when using Integer.MAX_VALUE (JVM BUG)?

Nesting generics into generics

String Bug or Compiler Bug?

H2 database: Is NIO JVM bug message related to H2 (and possibly database corruption)?

Generics Java - Get type of generics of generics