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

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

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 apples = Arrays.asList(new Apple()); List 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() ,则exception消失。

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

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

只要涉及交集类型并且您使用的方法引用使用的方法引用不是第一个类型(第一种类型是在类型擦除后将保留的类型),就会出现问题。

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

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

已编译的代码将与您的原始代码相同,但flatMap操作的正式结果类型将为Stream ,忽略推断的交集类型的所有其他工件。 因此,方法引用Fruit::getPickingMonth将实现类型Function而不是Function ,并且编译器错误不会实现。

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

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

实现同样的目标。

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

即:

 Fruit::getPickingMonth; //cant return anything 

我想你会想要类似的东西

 Apple::getPickingMonth; or Orange::getPickingMonth; 

代替

如果以上不是解决方案,那么编译器可能不知道在字节码级别返回什么。

StackOverflow上有类似的问题

Lambda Referencing

Lambda转换例外