gpt4 book ai didi

Java Casting : Java 11 throws LambdaConversionException while 1. 8 没有

转载 作者:IT老高 更新时间:2023-10-28 20:25:33 25 4
gpt4 key购买 nike

以下代码在 Java 1.8 VM 中运行良好,但在 Java 11 VM 中执行时会产生 LambdaConversionException。区别在哪里,为什么会这样?


代码:

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

HasValue Javadoc

异常(exception)(仅限 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

解决方法:

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

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

最佳答案

TL;DR Eclipse 编译器为根据规范无效的 lambda 实例生成方法签名。由于在 JDK 9 中添加了额外的类型检查代码以更好地执行规范,不正确的签名现在在 Java 11 上运行时会导致异常。


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

即使使用 null作为接收者,代码在引导时失败并出现同样的错误。

使用 javap -v Main我们可以看到问题出在哪里。我在 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

注意最后一个参数(常数#54)是(Ljava/lang/Object;)V , 而 javac生成 (Lmain/HasValue$ValueChangeEvent;)V .即 Eclipse 想要用于 lambda 的方法签名与 javac 不同。想用。

如果想要的方法签名是目标方法的删除(似乎是这样),那么正确的方法签名确实是(Lmain/HasValue$ValueChangeEvent;)V因为那是目标方法的删除,即:

void valueChanged(E event);

在哪里 EE extends HasValue.ValueChangeEvent<?> , 这样就会被删除为 HasValue.ValueChangeEvent .

问题似乎出在 ECJ 上,并且似乎已被 JDK-8173587 暴露出来。 ( revision ) (不幸的是,这似乎是一张私有(private)票。)它添加了额外的类型检查以验证 SAM 方法类型实际上与实例化方法类型兼容。根据 LambdaMetafactory::metafactory 的文档实例化的方法类型必须相同,或者是 SAM 方法类型的特化:

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.

ECJ生成的方法类型显然不是,所以最终抛出异常。 (不过,公平地说,在这种情况下,我没有看到任何地方定义了什么构成“特化”)。我已经在 Eclipse bugzilla 上报告了这一点:https://bugs.eclipse.org/bugs/show_bug.cgi?id=546161

我猜这个更改是在 JDK 9 的某个地方进行的,因为那时源代码已经是模块化的,而且修订日期相当早(2017 年 2 月)。

自从 javac生成正确的方法签名,您可以暂时切换到该方法签名作为解决方法。

关于Java Casting : Java 11 throws LambdaConversionException while 1. 8 没有,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55532055/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com