奇怪的例外"无效的接收器类型类java.lang.Object;不是...的子类型"

ant*_*tak 13 java java-8 java-stream

我在使用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
Run Code Online (Sandbox Code Playgroud)

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

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);
}
Run Code Online (Sandbox Code Playgroud)

如果我换Fruit::getPickingMonth到,那个例外就会消失x -> x.getPickingMonth().

对于它的价值:如果我Serializable从任一课程中删除,例外情况也会消失.但是,如果我为这两个类添加另一个等效接口,例如Cloneable或某些自定义接口,则返回.

Hol*_*ger 16

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

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

因此,当您使用lambda表达式替换方法引用时,您不再受该错误的影响.如果Serializable从类型中删除,则推断的元素类型Stream将是Fruit,即不是交集类型,并且不再发生问题.但与这两个元件型的执行FruitSerializable,编译器将推断的元素类型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);
Run Code Online (Sandbox Code Playgroud)

已编译的代码将与您的原始代码相同,但操作的正式结果类型flatMapStream<Fruit>忽略推断的交集类型的所有其他工件.因此,方法引用Fruit::getPickingMonth将实现类型Function<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);
Run Code Online (Sandbox Code Playgroud)

实现同样的目标.