2015-01-22 13 views
7

Durante l'aggiornamento di un'app in Java 8 mi sono imbattuto in un problema strano con google guava newArrayList in un paio di punti.Confusione del compilatore Java 8 Con metodi sovraccaricati

Date un'occhiata a questo esempio:

import com.google.common.collect.UnmodifiableIterator; 

import javax.naming.NamingException; 
import javax.naming.directory.Attribute; 
import javax.naming.directory.BasicAttribute; 
import java.util.ArrayList; 

import static com.google.common.collect.Iterators.forEnumeration; 
import static com.google.common.collect.Lists.newArrayList; 

public class NewArrayListIssue { 
    public static void main(String[] args) throws NamingException { 

     UnmodifiableIterator<?> elements = forEnumeration(getEnumeration().getAll()); 
     System.out.println("declarefirst = " + newArrayList(elements)); // calls newArrayList(Iterator<? extends E> elements) 

     ArrayList directCopy = newArrayList(forEnumeration(getEnumeration().getAll())); 
     System.out.println("useDirectly = " + directCopy); //calls newArrayList(E... elements) 
    } 

    public static Attribute getEnumeration(){ 
     return new BasicAttribute("foo",1); 
    } 
} 

Nel primo esempio quando ho la UnmodifiableIterator prima nella propria variabile e quindi chiamare newArrayList ottengo quello che mi aspetto, che è i valori Iteratori copiati in una nuovo List.

Nel secondo esempio in cui il forEnumeration va direttamente nel metodo newArrayList torno un List con una contenente all'iteratore (che contiene il valore).

Secondo Intellij si pensa che entrambe le chiamate di metodo dovrebbe essere quello di newArrayList(Iterator<? extends E> elements) ma ho trovato durante il debug che la seconda chiamata viene effettivamente newArrayList(E... elements).

Accade solo quando compilo con Oracle JDK8 indirizzato a Java8. Se bersaglio a 7, funziona bene.

risposta

7

Il problema è che il compilatore pensa che newArrayList(Iterator<? extends E>) non è applicabile (forse a causa di this bug) e quindi sceglie in silenzio il metodo varargs generica che è sempre applicabile (che indica il pericolo di tale sovraccarico), quando non si utilizza un tipo di elemento specifico per la lista dei risultati.

Il bug appare con i tipi di jolly, vale a dire nel codice è Attribute.getAll() restituire un NamingEnumeration<?> da qui il risultato di forEnumeration è UnmodifiableIterator<?> cui il compilatore si rifiuta di assegnare al Iterable<? extends E>, il tipo di parametro di newArrayList. Se si trasmette il valore restituito della chiamata interna a Enumeration, il problema scompare come avviene quando si trasmette il valore restituito della chiamata esterna a Iterator.

Non vedo una soluzione a breve termine semplice per questo problema. Dopo tutto, non capisco il motivo per cui non è stato utilizzato List<?> directCopy=Collections.list(getEnumeration().getAll()); in primo luogo ...

Si noti che se si desidera trovare tutte le occorrenze di questo problema, si può semplicemente utilizzare una versione modificata di guava in cui ha newArrayList(E...) stato rimosso e controllare tutti gli errori del compilatore (supponendo che non ci siano molti casi in cui si voglia veramente chiamare questo sovraccarico). Dopo aver riscritto i siti di chiamata, è possibile tornare al guava originale.

+0

È un grande numero di codice con molti autori. Quindi non posso rispondere perché un modo è stato scelto rispetto ad un altro. Ho solo bisogno di trovarli tutti. Grazie per il suggerimento sulla patch guava. Non avevo pensato di farlo. Credo di sapere cosa sto facendo stamattina. – ryber

+0

L'unico problema con la rimozione di newArrayList (E ...) è che una volta fatto il compilatore inizia a usare quello giusto. Quindi sembra che l'unica cosa veramente sicura sia di valutarli rapidamente tutti. – ryber

+1

Questo è strano perché il motivo per non usare quello giusto è che il compilatore pensa che quello giusto non sia applicabile e che non dovrebbe cambiare improvvisamente quando quello sbagliato è stato rimosso. Quando ho provato il trucco con Netbeans ha funzionato come previsto, ma potrebbe essere diverso con IntelliJ che, come hai detto, avrebbe raccolto il giusto obiettivo di invocazione. Ma ho pensato che se IntelliJ e il javac sottostante non erano d'accordo, IntelliJ non avrebbe nascosto gli errori di javac. Ha effettivamente generato file di classe? – Holger

1

L'ho visto accadere con metodi sovraccaricati e tipi generici. In questo caso viene scelta la versione più generica di newArrayList() quando il parametro non viene digitato esplicitamente.

Non ho una spiegazione tecnica per voi, ma vi consiglio di forzare l'utilizzo del sovraccarico metodo desiderato dal casting:

ArrayList directCopy = newArrayList((Iterator)forEnumeration(getEnumeration().getAll())); 
+0

Ah ma questo significa che devo trovarli tutti per primi! – ryber

+0

Hehe bummer:/Questo dovrebbe essere solo un "trova tutti i riferimenti" poiché IntelliJ sta determinando correttamente quelli giusti? – gknicker

+0

Avrei pensato ma Intellij è strano e mi restituisce tutte le variazioni. Plus newArrayList (Iterator è abbastanza comune poiché viene spesso utilizzato in combinazione con Filter e così via.Quindi ho bisogno di andare da loro e trovare quelli con i generici ambigui (fuori dal 600) – ryber

Problemi correlati