2014-10-06 13 views
15

Ho la seguente classe:Sovraccarico ambiguo in Java8: ECJ o javac sono corretti?

import java.util.HashSet; 
import java.util.List; 

public class OverloadTest<T> extends HashSet<List<T>> { 
    private static final long serialVersionUID = 1L; 

    public OverloadTest(OverloadTest<? extends T> other) {} 

    public OverloadTest(HashSet<? extends T> source) {} 

    private OverloadTest<Object> source; 

    public void notAmbigious() { 
    OverloadTest<Object> o1 = new OverloadTest<Object>(source); 
    } 

    public void ambigious() { 
    OverloadTest<Object> o2 = new OverloadTest<>(source); 
    } 
} 

Questo compila bene sotto javac del JDK 7, così come Eclipse (con il rispetto impostata su 1.7 o 1.8). Tuttavia, il tentativo di compilare sotto javac del JDK 8, ricevo il seguente errore:

[ERROR] src/main/java/OverloadTest.java:[18,35] reference to OverloadTest is ambiguous 
[ERROR] both constructor <T>OverloadTest(OverloadTest<? extends T>) in OverloadTest and constructor <T>OverloadTest(java.util.HashSet<? extends T>) in OverloadTest match 

Si noti che questo errore si applica solo al costruttore di invocazione del metodo ambigous(), non quello nel metodo notAmbiguous(). L'unica differenza è che ambiguous() si basa sull'operatore di diamante.

La mia domanda è questa: javac sotto JDK 8 segnala correttamente una risoluzione ambigua o javac sotto JDK 7 non riesce a cogliere un'ambiguità? A seconda della risposta, ho bisogno di presentare un bug JDK o un bug di ecj.

+0

Direi piuttosto un bug JDK, poiché sembra che si rompa la compatibilità con le versioni precedenti! – macias

+2

Non ricordo i dettagli, ma se ricordo male c'era un bug in JDK 7 dove qualcosa di simile era permesso per errore, e JDK 8 ha controlli più severi. Quindi sì, un'interruzione nella retrocompatibilità, ma di proposito. – Jesper

+0

Per ulteriori dettagli, vedere [Incompatibilità tra JDK 8 e JDK 7] (http://www.oracle.com/technetwork/java/javase/8-compatibility-guide-2156366.html#A999387) nella documentazione di Oracle. – Jesper

risposta

3

Nell'invocazione, quando il costruttore viene chiamato con T impostato esplicitamente, non c'è ambiguità:

OverloadTest<Object> o1 = new OverloadTest<Object>(source); 

Poiché T è definito al momento della chiamata al costruttore, oggetto passa il? estende il controllo dell'oggetto al momento della compilazione e non ci sono problemi. Quando T è impostata in modo esplicito di opporsi, le scelte per i due costruttori diventano:

public OverloadTest(OverloadTest<Object> other) {} 
public OverloadTest(HashSet<Object> source) {} 

E in questo caso, è molto facile per il compilatore di scegliere il primo. Nell'altro esempio (usando l'operatore diamond) T non è impostato in modo esplicito, quindi il compilatore prima tenta di determinare T controllando il tipo del parametro attuale, che la prima opzione non ha avuto bisogno di fare.

Se il secondo costruttore è stato cambiato per riflettere adeguatamente quello che immagino è l'operazione desiderata (che dal OverloadTest è un HashSet di liste di T, passando poi in un HashSet di liste di T dovrebbe essere possibile) in questo modo:

public OverloadTest(HashSet<List<? extends T>> source) {} 

... quindi l'ambiguità è stata risolta. Ma allo stato attuale ci sarà il conflitto quando chiederai al compilatore di risolvere quella invocazione ambigua.

Il compilatore vedrà l'operatore di diamante e tenterà di risolvere T in base a ciò che è stato passato e ciò che i vari costruttori si aspettano. Ma il modo in cui viene scritto il costruttore HashSet garantirà che indipendentemente da quale classe viene passato, entrambi i costruttori rimarranno validi, perché dopo la cancellazione, T viene sempre sostituito con Object. E quando T è Object, il costruttore HashSet e il costruttore OverloadTest hanno cancellazioni simili perché OverloadTest è un'istanza valida di HashSet. E poiché l'unico costruttore non sovrascrive l'altro (poiché OverloadTest T > non estende HashSet <T>), non si può dire che uno sia più specifico dell'altro, quindi non saprà come fare una scelta, e verrà invece eseguito un errore di compilazione.

Questo si verifica solo perché utilizzando T come limite si impone al compilatore di eseguire il controllo dei tipi. Se lo facessi semplicemente <? > anziché <? estende T > che compilerebbe bene. Il compilatore Java 8 è più rigido sui tipi e la cancellazione rispetto a Java 7, in parte perché molte delle nuove funzionalità di Java 8 (come i metodi di difesa dell'interfaccia) richiedevano che fossero un po 'più pedanti dei generici. Java 7 non stava riportando correttamente queste cose.

+0

Questa risposta non mostra come ** JLS richiede ** un compilatore per segnalare l'ambiguità. Essere più specifici è ** non ** definito in termini di override (vedi JLS 15.12.2.5 e JLS 18.5.4 (nuovo in Java 8)). Quindi credo che questa risposta sia ** errata **. –

0

Siamo spiacenti di rovinare la festa, ma la risoluzione del sovraccarico in Java è più complessa dei commenti e delle risposte viste finora.

Entrambi i costruttori sono applicabili:

OverloadTest(OverloadTest<? extends T>) 
OverloadTest(HashSet<? extends T>) 

A questo punto Java 8 impiega "More Specific Method Inference", che analizza tutte le coppie di candidati.

Durante il controllo, se l'ex costruttore è più specifico di quest'ultimo, il seguente vincolo è risolto con successo (T # 0 è una variabile deduzione per il parametro tipo T di HashSet):

OverloadTest<? extends T> <: HashSet<? extends T#0> 

Il la soluzione è di istanziare T # 0 a List<T>.

Il tentativo inversa non riesce a risolvere il seguente vincolo (qui T # 0 è una variabile deduzione per il parametro tipo T di OverloadTest):

HashSet<? extends T> <: OverloadTest<? extends T#0> 

No di istanze per T # 0 può essere trovato per soddisfare tale vincolo.

Poiché una direzione ha esito positivo e l'altra direzione non riesce, il precedente costruttore è considerato più specifico del secondo, risolvendo così l'ambiguità.

Ergo: accettare il programma sembra essere la risposta corretta dal compilatore.

PS: Non sto discutendo contro un controllo di tipo più pedante in Java 8 se necessario, ma non è questo il caso.

Problemi correlati