带有泛型的 LambdaConversionException:JVM 错误? [英] LambdaConversionException with generics: JVM bug?

查看:23
本文介绍了带有泛型的 LambdaConversionException:JVM 错误?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一些带有方法引用的代码,可以正常编译但在运行时失败.

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

例外是这样的:

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)

触发异常的类:

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

尝试解析 entity::filename 时抛出异常.filename()HasImagesEntity 中声明.据我所知,我得到了异常,因为 E 的擦除是 BasicEntity 并且 JVM 没有(不能?)考虑 E 的其他边界.

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.

当我将方法引用重写为一个简单的 lambda 时,一切都很好.对我来说,一个构造按预期工作并且其语义等价物爆炸了,这对我来说似乎很可疑.

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.

推荐答案

这里是一个简单的例子,它重现了问题并且只使用了核心 Java 类:

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

您的假设是正确的,特定于 JRE 的实现接收目标方法作为 MethodHandle 没有关于泛型类型的信息.因此,它唯一看到的是原始类型不匹配.

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.

与许多泛型结构一样,字节码级别需要一个类型转换,它不会出现在源代码中.由于 LambdaMetafactory 明确要求一个直接方法句柄,封装了这种类型转换的方法引用不能作为MethodHandle传递给工厂.

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.

第一个解决方案是更改 LambdaMetafactory 以信任 MethodHandle 如果接收器类型是 interface 并插入所需的类型转换自己在生成的 lambda 类中,而不是拒绝它.毕竟,它已经对参数和返回类型做了类似的事情.

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.

或者,编译器将负责创建一个合成的辅助方法来封装类型转换和方法调用,就像您编写了 lambda 表达式一样.这不是一个独特的情况.如果您使用对 varargs 方法或数组创建的方法引用,例如String[]::new,它们不能被表示为直接方法句柄并最终出现在合成辅助方法中.

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.

无论哪种情况,我们都可以将当前行为视为错误.但显然,编译器和 JRE 开发人员必须就应该以何种方式处理它达成一致,然后我们才能说出错误位于哪一边.

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.

这篇关于带有泛型的 LambdaConversionException:JVM 错误?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆