2015-11-21 32 views
7

Nota: ho trovato più domande che evidenziavano le differenze tra javac e il compilatore Eclipse, ma per quanto ho potuto vedere tutti discutono di altri problemi.Generics e lambdas: comportamento diverso nel compilatore javac ed Eclipse

Supponiamo di avere questo metodo:

public static <T, U> void foo(Supplier<T> a, Function<T, U> b, Consumer<U> c) 
{ 
    c.accept(b.apply(a.get())); 
} 

ho trovato un comportamento differente tra javac e il compilatore Eclipse Java durante la compilazione chiamate a questo metodo e non sono sicuro quale dei due ha ragione.


Un semplice uso di questo metodo potrebbe essere:

// variant 1 
foo(
    () -> Optional.of("foo"), 
    value -> value.get(), 
    value -> System.out.println(value)); 

Il compilatore dovrebbe essere in grado di legarsi al TOptional<String> utilizzando il primo argomento e U al String utilizzando il secondo. Quindi questa chiamata dovrebbe essere valida (secondo me).

Questo compila bene usando javac ma non riesce a compilare utilizzando Eclipse:

Tipo non corrispondente: impossibile convertire da vuoto a <Sconosciuto>

Aggiunta di un argomento di tipo al primo argomento (() -> Optional.<String> of("foo")) lo rende anche in Eclipse.

Domanda: Da un punto di vista delle specifiche, Eclipse è corretto nel rifiutare questa chiamata (e perché (non))?


Ora supponiamo che vogliamo lanciare un (runtime) eccezione personalizzata, se il Optional è vuota:

// variant 2 
foo(
    () -> Optional.of("foo"), 
    value -> value.orElseThrow(() -> new RuntimeException()), 
    value -> System.out.println(value)); 

Questo è respinta da entrambi, javac e il compilatore Eclipse, ma con diversi messaggi di errore :

  • javac: "eccezione X non dichiarata; deve essere catturato o dichiarato di essere gettato"
  • Eclipse compilatore: "Tipo non corrispondente: non può convertire da vuoto a <sconosciuto>"

Quando aggiungo l'argomento di tipo al primo argomento di cui sopra, Eclipse riesce a compilare mentre javac fallisce ancora. Quando aggiungo <RuntimeException> come argomento di tipo al secondo argomento, è il contrario, Eclipse ha esito negativo e javac ha esito positivo.

Domanda: Ancora una volta, i compilatori hanno ragione nel rifiutare questa chiamata e perché?


A mio parere entrambe le varianti devono compilare correttamente senza ulteriori suggerimenti utilizzando argomenti tipo. In tal caso, compilerò una segnalazione di bug per javac (riguardante la "eccezione non segnalata") e una per il compilatore di Eclipse (riguardante la "mancata corrispondenza del tipo"). Ma prima voglio essere sicuro che le specifiche condividano il mio punto di vista.

Versioni utilizzati:

  • javac: 1.8.0_66
  • Eclipse JDT: 3.11.1.v20151118-1100

EDIT:

ho riempito bug 482781 per il problema in Eclipse.

Il problema con javac è già segnalato come JDK-8056983, vedere Tunakis answer.

+1

Incolpare l'eclissi in caso di dubbio :) L'inferenza del tipo è molto complicata, l'intera sintassi lambda è ancora abbastanza nuova. Eclipse ha corretto diversi bug, ma alcuni sono stati lasciati nella versione corrente, relativi a casi limite come questo. – zapl

+1

Il compilatore ECJ di Eclipse Mars è davvero buggato rispetto all'ultima Luna quando si tratta di espansione generica. Ho già inciampato in almeno tre casi in cui ECJ 3.11 fallisce o addirittura si blocca in loop infinito mentre javac ed ECJ 3.10 si compilano correttamente. Ecco perché sto ancora usando Luna. –

+0

Il bug di Eclipse è già stato risolto per 4.6 M1 tramite https://bugs.eclipse.org/470826 che è anche programmato per il port back to mars.2 –

risposta

5

Sì, hai ragione in ogni aspetto. Onestamente non sarei in grado di collegarmi a linee specifiche del JLS al riguardo: inferenza di tipo is a whole chapter.

Disclaimer: Ho eseguito il test utilizzando Eclipse Mars 4.5.1 e JDK 1.8.0_60.


Variante 1 deve compilare e Eclipse ha un bug qui. Non sono riuscito a trovare nulla relativo a questo nel loro Bugzilla in modo da poter andare avanti e archiviarlo. Si può assicurare che dovrebbe compilare se si riduce il vostro esempio a questo:

public static <T> void foo(Supplier<T> a) { 
    a.get(); 
} 

foo(() -> Optional.of("foo")); 

Questo compila bene sia con Eclipse e javac. L'aggiunta di parametri non dovrebbe (dovrebbe) modificare il tipo di derivazione per T durante la compilazione.


Variante 2 non compila per javac e questo è davvero un bug, come riportato nel JDK-8056983. Il compilatore dovrebbe essere in grado di dedurre che X è RuntimeException. Per quanto riguarda il motivo per cui Eclipse non è ancora in grado di compilarlo, ancora una volta, non ho trovato nulla nel loro Bugzilla, quindi sentitevi liberi di segnalarlo!

+1

La correzione per https://bugs.eclipse.org/470826 consente ad Eclipse di accettare entrambe le varianti. –