简体   繁体   English

Java Casting:Java 11抛出LambdaConversionException而1.8抛出LambdaConversionException

[英]Java Casting: Java 11 throws LambdaConversionException while 1.8 does not

the following code works perfectly fine in a Java 1.8 VM but produces a LambdaConversionException when executed in a Java 11 VM. 以下代码在Java 1.8 VM中完美运行,但在Java 11 VM中执行时会产生LambdaConversionException Wheres the difference and why does it behave like this? 差异是什么,为什么它会像这样?


Code: 码:

public void addSomeListener(Component comp){
    if(comp instanceof HasValue) {
        ((HasValue<?,?>) comp).addValueChangeListener(evt -> {
            //do sth with evt
        });
    }
}

HasValue Javadoc HasValue Javadoc

Exception (V11 only): 例外(仅限V11):

Caused by: java.lang.invoke.LambdaConversionException: Type mismatch
for instantiated parameter 0: class java.lang.Object is not a subtype
of interface com.vaadin.flow.component.HasValue$ValueChangeEvent
    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.altMetafactory(LambdaMetafactory.java:503)
    at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:138)
    ... 73 more

Workaround: 解决方法:

ValueChangeListener<ValueChangeEvent<?>> listener = evt -> {
    // do sth with evt
};
((HasValue<?,?>) comp).addValueChangeListener(listener);

System: 系统:
OS: Windows 10 操作系统:Windows 10
IDE: Eclipse 2018-12 (4.10.0) IDE:Eclipse 2018-12(4.10.0)
Java (Compile): ecj Java(编译):ecj
Java (Webserver): JDK 11.0.2 Java(Webserver):JDK 11.0.2
Webserver: Wildfly 15 网络服务器:Wildfly 15

TL;DR The Eclipse compiler generates a method signature for the lambda instance that is invalid according to the specification. TL; DR Eclipse编译器根据规范为lambda实例生成一个无效的方法签名。 Due to additional type checking code added in JDK 9 to better enforce the specification, the incorrect signature is now causing an exception when running on Java 11. 由于JDK 9中添加了额外的类型检查代码以更好地实施规范,因此在Java 11上运行时,错误的签名现在会导致异常。


Verified with Eclipse 2019-03 as well with this code: 使用此代码验证Eclipse 2019-03以及:

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);
}

Even when using null as the receiver, the code fails when bootstrapping with the same error. 即使使用null作为接收器,代码也会在引导时出现相同的错误。

Using javap -v Main we can see where the problem lies. 使用javap -v Main我们可以看到问题所在。 I'm seeing this in the BoostrapMethods table: 我在BoostrapMethods表中看到了这个:

BootstrapMethods:
  0: #48 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:
      #50 (Lmain/HasValue$ValueChangeEvent;)V
      #53 REF_invokeStatic main/Main.lambda$0:(Ljava/lang/Object;)V
      #54 (Ljava/lang/Object;)V

Note that the last argument (constant #54) is (Ljava/lang/Object;)V , while javac generates (Lmain/HasValue$ValueChangeEvent;)V . 注意,最后一个参数(常量#54)是(Ljava/lang/Object;)V ,而javac生成(Lmain/HasValue$ValueChangeEvent;)V ie the method signature that Eclipse wants to use for the lambda is different from what javac wants to use. 即,Eclipse想要用于lambda的方法签名与javac想要使用的方法签名不同。

If the wanted method signature is the erasure of the target method (which seems to be the case), then the correct method signature is indeed (Lmain/HasValue$ValueChangeEvent;)V since that is the erasure of the target method, which is: 如果想要的方法签名是目标方法的擦除(似乎是这种情况),那么正确的方法签名确实是(Lmain/HasValue$ValueChangeEvent;)V因为这是目标方法的擦除,即:

void valueChanged(E event);

Where E is E extends HasValue.ValueChangeEvent<?> , so that would be erased to HasValue.ValueChangeEvent . 其中EE extends HasValue.ValueChangeEvent<?> ,因此将被删除为HasValue.ValueChangeEvent

The problem seems to be with ECJ, and seems to have been brought to the surface by JDK-8173587 ( revision ) (Unfortunately this seems to be a private ticket.) which adds extra type checks to verify that the SAM method type is actually compatible with the instantiate method type. 这个问题似乎与ECJ有关,似乎已经被JDK-8173587修订版 )带到了表面(不幸的是这似乎是一张私人票。)它增加了额外的类型检查来验证SAM方法类型是否真正兼容使用实例化方法类型。 According to the documentation of LambdaMetafactory::metafactory the instantiated method type must be the same, or a specialization of the SAM method type: 根据LambdaMetafactory::metafactory的文档,实例化的方法类型必须相同,或SAM方法类型的特化:

instantiatedMethodType - The signature and return type that should be enforced dynamically at invocation time. instantiatedMethodType - 应在调用时动态强制执行的签名和返回类型。 This may be the same as samMethodType, or may be a specialization of it. 这可能与samMethodType相同,也可能是它的特化。

which the method type generated by ECJ is evidently not, so this ends up throwing an exception. ECJ生成的方法类型显然不是,所以这最终会引发异常。 (though, to be fair, I don't see defined anywhere what constitutes a "specialization" in this case). (但是,公平地说,我没有看到在这种情况下什么构成“专业化”的任何地方)。 I've reported this on the Eclipse bugzilla here: https://bugs.eclipse.org/bugs/show_bug.cgi?id=546161 我在Eclipse bugzilla上报告了这个: https ://bugs.eclipse.org/bugs/show_bug.cgi id = 546161

I'm guessing this change was made somewhere in JDK 9, since source code was already modular at that point, and the date of the revision is fairly early (February 2017). 我猜这个改变是在JDK 9中的某个地方进行的,因为源代码在那时已经是模块化的,并且修订日期相当早(2017年2月)。

Since javac generates the correct method signature, you could switch to that for the time being as a workaround. 由于javac生成了正确的方法签名,因此您可以暂时将其切换为解决方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 并行流() java 1.8 与 11 - parallelStream() java 1.8 vs 11 Gradle 检测到 Java 1.8 而不是 Java 11 - Gradle detects Java 1.8 instead of Java 11 为什么在Java 1.8中进行此类排序 - Why does this sort in Java 1.8 Appium 测试使用 java 11 执行,而 uiautomatorviewer 抛出错误。 java 11 是否支持 uiautomatorviewer? - Appium tests executes with java 11 while uiautomatorviewer throws an error. Is uiautomatorviewer supported by java 11? 如何使用IntelliJ中的Java 11目标Java 1.8进行编译 - How to compile with Java 11 target Java 1.8 in IntelliJ 将现有 Spring 引导项目从 java 1.8 更新到 java 11 - Update existing Spring Boot project from java 1.8 to java 11 为什么隐式转换在反射投射抛出异常时有效? - Why does implicit casting work while reflection casting throws exception? 在Java上将文件解压缩时升级到1.8时出现的问题 - Issue while unzipping file on java upgrade to 1.8 从 JavaFX 1.8 迁移到 Java 11 + JavaFX 和 Z91157F709804ACZF742 - Migrating from JavaFX 1.8 to Java 11 + JavaFX and Maven 使用 Cordova 的 JDK 1.8 或更高版本的 Java 11 要求检查失败 - Java 11 requirements check failed for JDK 1.8 or greater with Cordova
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM