2015-11-26 35 views
5

我使用jre1.8.0_66獲取代碼來看,這種奇怪的例外:古怪異常「無效的接收器類型類java.lang;不是亞型......」

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

它的價值:如果我從兩個類中刪除Serializable,例外情況也會消失。但是,如果我爲這兩個類添加了另一個等效接口,則返回。 Cloneable或一些自定義界面。

+1

這似乎與[這篇文章](http://stackoverflow.com/q/33925551/1743880)是一個'javac'的錯誤。 – Tunaki

回答

7

你遇到了已在this questionthat question討論過相同的編譯器錯誤這樣的問題。

問題每當一個交叉點型涉及並且使用的是比第一個之外的接收器類型(第一類型是一個將保持類型擦除之後)使用的方法引用發生。

所以,當你更換一個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); 

編譯代碼將等同於原來的,但flatMap操作的正式結果類型將是Stream<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); 

實現相同。

+0

感謝您的解釋!我無法跟隨其他兩個討論,但這個答案簡潔而且非常容易理解。 – antak

+0

@Holger感謝您的詳細解釋。 –

0

我相信你只是試圖從沒有返回的接口引用的方法。

即:

Fruit::getPickingMonth; //cant return anything 

我可以想象你會希望像

Apple::getPickingMonth; 
or 
Orange::getPickingMonth; 

相反

如果上面的是不是可能是解決鑄造問題,即編譯器不知道在字節碼級別返回什麼。

有StackOverflow上

Lambda Referencing

Lambda Conversion Exception

相關問題