2015-11-26 4 views
5

sto ricevendo questa strana eccezione nel codice eseguito utilizzando jre1.8.0_66:eccezione strano "non valido di classe tipo di ricevitore java.lang.Object, non un sottotipo di ..."

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 

Che significa ? Il codice è il seguente:

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); 
} 

L'eccezione va via se cambio Fruit::getPickingMonth a x -> x.getPickingMonth().

Per quello che vale: Anche l'eccezione scompare se rimuovo Serializable da una classe. Ma restituisce se aggiungo un'altra interfaccia equivalente a entrambe le classi, ad es. Cloneable o qualche interfaccia personalizzata.

+1

Questo sembra molto correlato a [questo post] (http://stackoverflow.com/q/33925551/1743880) che è un bug 'javac'. – Tunaki

risposta

7

È stato eseguito nello stesso bug del compilatore che è stato discusso in this question e that question.

Il problema si verifica quando è coinvolto un tipo di intersezione e si utilizza un riferimento al metodo utilizzando un tipo di ricevitore diverso dal primo (il primo tipo è quello che rimarrà dopo la cancellazione del tipo).

Così quando si sostituisce il riferimento al metodo con un'espressione lambda, non si ha più alcun problema con il bug. Se si rimuove il tipo Serializable dai tipi, il tipo di elemento dedotto di Stream sarà Fruit, vale a dire non un tipo di intersezione e, di nuovo, il problema non si verifica. Ma con i due tipi di elementi che implementano Fruit e Serializable, il compilatore dedurrà il tipo di elemento Object&Fruit&Serializable e il tipo non elaborato sarà Object che provoca l'errore quando si utilizza un riferimento al metodo con il tipo di ricevitore Fruit. Si può facilmente aggirare questo:

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

Il codice compilato sarà identico all'originale, ma il tipo di risultato formale dell'operazione flatMap sarà Stream<Fruit>, ignorando tutti gli altri manufatti del tipo di intersezione dedotto. Di conseguenza, il metodo di riferimento Fruit::getPickingMonth implementerà il tipo Function<Fruit,Integer> anziché Function<Object&Fruit&Serializable,Integer> e il bug del compilatore non si materializzerà.

Ma si noti che il codice è inutilmente complicato. È possibile utilizzare semplicemente

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

per ottenere lo stesso risultato.

+0

Grazie per la spiegazione! Non ho potuto seguire le altre due discussioni, ma questa risposta è concisa ed estremamente facile da capire. – antak

+0

@Holger grazie per la tua spiegazione molto dettagliata. –

0

Credo che stai solo provando a fare riferimento a un metodo dall'interfaccia senza ritorno.

cioè:

Fruit::getPickingMonth; //cant return anything 

mi immagino che si desidera qualcosa di simile

Apple::getPickingMonth; 
or 
Orange::getPickingMonth; 

Invece

Se quanto sopra non è la soluzione potrebbe essere un problema di colata in cui il compilatore non sa cosa restituire a livello di bytecode.

Non ci sono domande come questo su StackOverflow

Lambda Referencing

Lambda Conversion Exception