奇怪的异常“无效的接收器类型类java.lang.Object;不是的子类型...”

2022-09-02 01:26:34

我在使用jre1.8.0_66的代码运行中得到这个奇怪的异常:

Exception in thread "main" java.lang.BootstrapMethodError: call site initialization exception
    at java.lang.invoke.CallSite.makeSite(CallSite.java:341)
    at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:307)
    at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:297)
    at main
Caused by: java.lang.invoke.LambdaConversionException: Invalid receiver type class java.lang.Object; not a subtype of implementation type interface Fruit
    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:302)
    ... 3 more

这是什么意思?代码如下:

public static interface Fruit {

    int getPickingMonth();
}

public static class Apple implements Fruit, Serializable {

    @Override
    public int getPickingMonth() {
        return 11;
    }
}

public static class Orange implements Fruit, Serializable {

    @Override
    public int getPickingMonth() {
        return 2;
    }
}

public static void main(String[] args) {

    List<Apple> apples = Arrays.asList(new Apple());
    List<Orange> oranges = Arrays.asList(new Orange());

    Stream.of(apples.stream(), oranges.stream())
            .flatMap(Function.identity())
            .map(Fruit::getPickingMonth)  // exception occurs on this line
            .forEachOrdered(System.out::println);
}

如果我更改为 ,则异常将消失。Fruit::getPickingMonthx -> x.getPickingMonth()

对于它的价值:如果我从任一类中删除,异常也会消失。但是,如果我向两个类添加另一个等效的接口,则返回,例如 或一些自定义界面。SerializableCloneable


答案 1

您遇到了与本问题和该问题中讨论过的相同的编译器错误。

每当涉及交集类型并且您使用的方法引用使用第一个类型以外的接收器类型(第一个类型是类型擦除后将保留的类型)时,就会出现此问题。

因此,当您将方法引用替换为 lambda 表达式时,您不再受 Bug 的影响。如果从类型中删除 ,则推断的元素类型将为 ,即不是交集类型,并且同样不会出现问题。但是,随着两个元素类型的实现和,编译器将推断元素类型,而原始类型将在将方法引用与接收器类型一起使用时引发错误。您可以轻松解决此问题:SerializableStreamFruitFruitSerializableObject&Fruit&SerializableObjectFruit

Stream.of(apples.stream(), oranges.stream())
        .<Fruit>flatMap(Function.identity())
        .map(Fruit::getPickingMonth) // no more exception on this line
        .forEachOrdered(System.out::println);

编译后的代码将与原始代码相同,但操作的正式结果类型将是 ,忽略推断的交集类型的所有其他工件。因此,方法引用将实现类型而不是类型,并且编译器 bug 不会实现。flatMapStream<Fruit>Fruit::getPickingMonthFunction<Fruit,Integer>Function<Object&Fruit&Serializable,Integer>

但请注意,您的代码是不必要的复杂。您可以简单地使用

Stream.<Fruit>concat(apples.stream(), oranges.stream())
        .map(Fruit::getPickingMonth) // no more exception on this line
        .forEachOrdered(System.out::println);

以实现相同的目标。


答案 2

我相信你只是试图从接口引用一个没有返回的方法。

结婚

Fruit::getPickingMonth; //cant return anything

我想你会想要这样的东西

Apple::getPickingMonth;
or
Orange::getPickingMonth;

相反

如果上述不是解决方案,则可能是转换问题,编译器不知道在字节码级别返回什么。

StackOverflow上有这样的问题

Lambda 引用

Lambda 转换异常


推荐