当前位置: 首页 > 知识库问答 >
问题:

带泛型的LambdaConversionException:JVM bug?

南宫奇思
2023-03-14

我有一些带有方法引用的代码,它编译得很好,但在运行时失败。

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

当我将方法引用重写为一个琐碎的lambda时,一切都很好。在我看来,一个结构如预期的那样工作,而它的语义等价物却爆炸了,这似乎真的很可疑。

这可能在规范中吗?我正在非常努力地寻找一种方法,使它不成为编译器或运行时的问题,但还没有提出任何问题。

共有1个答案

仉昂熙
2023-03-14

下面是一个简化的html" target="_blank">示例,它再现了这个问题,并且只使用核心Java类:

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的形式接收目标方法,该方法没有关于泛型类型的信息。因此,它唯一看到的是原始类型不匹配。

与许多泛型构造一样,在字节码级别上需要一个类型转换,而源代码中没有出现这种类型转换。由于LambdameTafactory显式要求直接方法句柄,因此封装此类类型强制转换的方法引用不能作为MethodHandle传递到工厂。

或者,编译器将负责创建一个封装类型转换和方法调用的合成帮助器方法,就像您编写了一个lambda表达式一样。这不是一个独特的情况。如果将方法引用用于varargs方法或数组创建,例如string[]::new,则不能将它们表示为直接方法句柄,而最终使用合成助手方法。

无论哪种情况,我们都可以将当前行为视为bug。但显然,编译器和JRE开发人员必须在确定bug位于哪一边之前,就应该以哪种方式处理它达成一致。

 类似资料:
  • 问题内容: 我有一个代表文本片段的泛型类。该文本片段可能具有多种不同模式(突出显示的不同类型)中的任何一种。这些模式用枚举表示。每个项目的Enum可能不同,但是它必须实现一个接口,该接口提供了一种将其中两个结合的方法(可以突出显示并加粗显示)。所以我有一个界面: 然后我的TextFragment是文本字符串和模式的容器。但是当我尝试声明该类时: 我收到以下错误: 令牌“扩展”的语法错误,预期 根据

  • 如何获取这个类的类型?对于上下文,我使用ModelMapper,我需要类类型T从S转换为T。 背景: 我已经尝试了N种方法,其中我放置了“//一些方法来获取类型”,但没有任何效果。例如: 或

  • 问题内容: 为什么这样做: 虽然这会产生类型不匹配错误: 为什么是这样?是否有办法解决此问题,而不使用原始类型? 问题答案: 尝试 注意:请注意,当您使用类似List的集合时,您只能在“只读”模式下使用它(添加空值除外)。

  • 嗨,我正在努力让这两种方法发挥作用,但即使我尝试了几乎所有我能想到的方法,它仍然不起作用。请告诉我怎么修! 空添加(任何值):将一个包含newValue的节点添加到列表的末尾。 空添加(int index,任何值):在索引的节点之后添加一个包含newValue的节点(假设索引从0开始)。 下面是我的代码:(上面的方法显示在底部)

  • Java中是否有一种方法可以通过一个方法的声明返回不同的类型? 我希望此方法返回一个对象,并在函数调用时将其转换为正确的类型。这就是我的想法,但它不是这样工作的。我是否需要某种通用返回类型来执行此操作?解决这个问题的最佳方法是什么?

  • 我在typescript中有以下泛型类 但是我不知道为什么得到这个错误Class'(匿名类)'不正确地扩展基类'列'。属性getValue的类型不兼容。类型'(值:数字)=