Community
Participate
Working Groups
Code to reproduce: ``` public class Main { public static void main(String[] args) { ((Widget<?>) new Widget<>()).addListener(evt -> {}); } } class Widget<E extends CharSequence> { void addListener(Listener<? super E> listener) {} } interface Listener<E extends CharSequence> { void m(E event); } ``` Which throws: ``` Exception in thread "main" java.lang.BootstrapMethodError: bootstrap method initialization exception at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:194) at java.base/java.lang.invoke.CallSite.makeSite(CallSite.java:307) at java.base/java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:258) at java.base/java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:248) at test/main.Main.main(Main.java:5) Caused by: java.lang.invoke.LambdaConversionException: Type mismatch for instantiated parameter 0: class java.lang.Object is not a subtype of interface java.lang.CharSequence at java.base/java.lang.invoke.AbstractValidatingLambdaMetafactory.checkDescriptor(AbstractValidatingLambdaMetafactory.java:308) at java.base/java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:294) at java.base/java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:328) at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:127) ... 4 more ``` inspecting `javap -v Main` shows following BSM entry: ``` BootstrapMethods: 0: #43 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; Method arguments: #45 (Ljava/lang/CharSequence;)V #48 REF_invokeStatic main/Main.lambda$0:(Ljava/lang/Object;)V #49 (Ljava/lang/Object;)V ``` Where `(Ljava/lang/Object;)V` is rejected by LambdaMetafactory::metafactory code since http://hg.openjdk.java.net/jdk/jdk/diff/ee6b5bd26bf9/jdk/src/java.base/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java According to the documentation of the LambdametaFactory::metafactory, the third (non-default) argument: "instantiatedMethodType - The signature and return type that should be enforced dynamically at invocation time. This may be the same as samMethodType, or may be a specialization of it." Although I don't see "specialization" defined, evidently `(Ljava/lang/Object;)V` is not valid as a specialization of `(Ljava/lang/CharSequence;)V`. javac generates `(Ljava/lang/CharSequence;)V` as the third argument of the BSM, which is also the erasure of `Listener::m`. Eclipse seems to be in the wrong here. See also discussion on Stack Overflow: https://stackoverflow.com/q/55532055
We are also seeing this on Ubuntu 18.04 with Oracle Java 12.0.2
Still experiencing this with the latest JDT (3.18.800.v20210611-1600).
It's somewhat frustrating that this doesn't event get acknowledged. This bug means that affected code compiled in Eclipse will not run in Java 11 or later. While this shouldn't affect actual builds, it means that if you want to run your code during development you have to add a cast to every affected call, basically polluting your code. BTW, this also affects VSCode since they also use the Eclipse Compiler.
(In reply to Patrick Schmidt from comment #3) > It's somewhat frustrating that this doesn't event get acknowledged. Now it is :-) Still fails on 4.22 master :-( Slightly changed snippet compiles and runs without issues (note the missing cast): public class Main { public static void main(String[] args) { new Widget<>().addListener(evt -> {}); } } class Widget<E extends CharSequence> { void addListener(Listener<? super E> listener) {} } interface Listener<E extends CharSequence> { void m(E event); }
Maybe that snippet wasn't the best choice to illustrate the bug, if the cast has to be added in order for it to fail, instead of the other way around :) The answer of the linked Stackoverflow question provides a better example in that respect: ``` public class Main { public static void main(String[] args) { getHasValue().addValueChangeListener(evt -> {}); } public static HasValue<?, ?> getHasValue() { return null; } } interface HasValue<E extends HasValue.ValueChangeEvent<V>,V> { public static interface ValueChangeEvent<V> {} public static interface ValueChangeListener<E extends HasValue.ValueChangeEvent<?>> { void valueChanged(E event); } void addValueChangeListener(HasValue.ValueChangeListener<? super E> listener); } ``` Adding the cast (ValueChangeListener<ValueChangeEvent<?>>) to evt will fix the issue.
The proposed PR at https://github.com/eclipse-jdt/eclipse.jdt.core/pull/1082 fixes this problem and I have pushed the tests from here to that PR.