2015-07-03 16 views
5

Mi sono imbattuto in alcuni, almeno per me, strano comportamento in Eclipse 4.4 e Java 8 build 45 durante l'esecuzione di un refactoring di espressioni di estrazione. L'esempio seguente mostra il codice originale e senza errori prima di applicare la refactoring estratto:Tipo mancata corrispondenza dopo l'estrazione di un'espressione con tipo di ritorno generico

import java.util.Map; 
import java.util.Set; 

public class MyMap<K, V> { 
    public void putAll(final Map<? extends K, ? extends V> mapToCopy) { 
     for (Map.Entry<? extends K, ? extends V> entry : mapToCopy.entrySet()) { 
     } 
    } 
} 

Il risultato del refactoring Eclipse assomiglia a questo e porta alla seguente messaggio di errore che fa riferimento al l'accesso in lettura di entrySet nel dichiarazione ciclo:

public void putAll(final Map<? extends K, ? extends V> mapToCopy) { 
     Set<?> entrySet = mapToCopy.entrySet(); 
     for (Map.Entry<? extends K, ? extends V> entry : entrySet) { 
                 ^^^^^^^^ 
     } 
    } 

Type mismatch: cannot convert 
    from element type capture#3-of ? 
    to Map.Entry<? extends K,? extends V> 

ho alterato il tipo della dichiarazione di entrySet a Set<Map.Entry<? extends K, ? extends V>>. Questa volta, l'errore viene indicato nella parte di inizializzazione della dichiarazione, dicendo:

public void putAll(final Map<? extends K, ? extends V> mapToCopy) { 
     Set<Map.Entry<? extends K, ? extends V>> entrySet = mapToCopy.entrySet(); 
                  ^^^^^^^^^^^^^^^^^^^^ 
     for (Map.Entry<? extends K, ? extends V> entry : entrySet) { 
     } 
    } 

Type mismatch: cannot convert 
    from Set<Map.Entry<capture#1-of ? extends K,capture#2-of ? extends V>> 
    to Set<Map.Entry<? extends K,? extends V>> 

Dal momento che il codice originale si compila, io sono un po 'perplesso. Forse qualcuno può aiutarmi e dare una spiegazione? Grazie in anticipo!

+1

Si noti che 'Set > entrySet = mapToCopy.entrySet(); 'funzionerà. [JLS § 14.4.2] (http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.14.2) parla della traduzione di 'Iterable' nell'elemento avanzato dichiarazione 'for'. Vedi anche [questa risposta] (http://stackoverflow.com/a/16753901/5065475) –

+0

@AndyBrown: puoi approfondire "Nota che [...] funzionerà", per favore. Cosa devo modificare per farlo funzionare? – Marcus

+0

In java8, provare anche 'map.forEach ((chiave, valore) -> {...})'. chiave/valore vengono dedotti a tipi appropriati, come alcuni sottotipi sconosciuti di K/V. L'API è anche abbastanza flessibile se dettiamo i tipi - 'map.forEach ((tasto K, valore V) -> {...})' – ZhongYu

risposta

0

Consente prima revisione fonte originale:

public void putAll(final Map<? extends K, ? extends V> mapToCopy) { 
    for (Map.Entry<? extends K, ? extends V> entry : mapToCopy.entrySet()) { 
    } 
} 

internamente (e in fase di esecuzione), questo sarà compilato e funzionerà come:

public void putAll(final Map mapToCopy) { 
    for (Iterator<Map.Entry> iterator = mapToCopy.iterator; iterator.hasNext();) { 
    } 
} 

dove ? extends K e ? extends V sarà sostituito con un po ' i tipi reali dopo la cancellazione del tipo. Il compilatore saprà quali sono questi tipi e non genererà un Exception per incompatibilità di tipo.

D'altra parte, se si refactoring la fonte di questo,

public void putAll(final Map<? extends K, ? extends V> mapToCopy) { 
    Set<Entry<? extends K, ? extends V>> entrySet = mapToCopy.entrySet(); 
                ^^^^^^^^ 
    for (Map.Entry<? extends K, ? extends V> entry : entrySet) { 

    } 
} 

poi il compilatore avrà alcuna prova che il entrySetstive dello stesso tipo di Map.Entry<? extends K, ? extends V>, semplicemente perché il carattere jolly (?) indica sempre qualcosa di sconosciuto, ovvero non è garantito che il carattere jolly dal valore della chiave di immissione dello entrySet sia lo stesso del valore della chiave entry (dal ciclo). Non essendo sicuro al cento per cento che i tipi siano compatibili, il compilatore solleva un errore in fase di compilazione **, anche se ** si può avere la certezza che questi tipi saranno uguali in fase di runtime.

+0

Grazie per la risposta. Perché il tuo terzo snippet di codice mostra l'errore indicato nell'istruzione loop? Non posso riprodurlo. Nel mio terzo snippet di codice, l'errore è indicato nella dichiarazione. – Marcus

+0

Penso che il problema non sia legato al ciclo for. Ho elaborato la domanda e ho iniziato un nuovo thread [qui] (http://stackoverflow.com/questions/32143844/type-mismatch-when-using-map-entryset). – Marcus

Problemi correlati