当前位置: 首页 > 面试题库 >

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

曹高轩
2023-03-14
问题内容

我在使用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::getPickingMonth为,异常会消失x -> x.getPickingMonth()

这样做的价值:
如果我Serializable从任一类中删除,该异常也将消失。但是如果我在两个类中都添加了另一个等效的接口(例如,Cloneable或某些自定义接口),则会返回


问题答案:

每当涉及交集类型并且您使用的方法引用使用的接收器类型不是第一个(第一个类型是在类型擦除之后将保留的类型)时,都会发生此问题。

因此,当您用lambda表达式替换方法引用时,您将不再受到该错误的影响。如果Serializable从类型中删除,则的推断元素类型Stream将为Fruit,即不是交集类型,同样不会发生此问题。但是,通过实现Fruit和的两种元素类型Serializable,编译器将推断出元素类型Object&Fruit&Serializable,而原始类型将是原始类型Object,当使用带有接收器类型的方法引用时,原始类型将引发错误Fruit。您可以轻松解决此问题:

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

编译后的代码将与您的原始代码相同,但是操作的正式结果类型flatMap将为Stream<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);

实现相同。



 类似资料: