2015-07-03 10 views
5

Ich stieß auf einige, zumindest für mich, seltsames Verhalten in Eclipse 4.4 und Java 8 Build 45 beim Ausführen eines Extrakt-Ausdruck Refactoring. Das folgende Beispiel zeigt das Original und fehlerfreien Code vor dem Extrakt Refactoring Anwendung:Typ Mismatch nach dem Extrahieren von Ausdruck mit generischen Rückgabetyp

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()) { 
     } 
    } 
} 

Das Ergebnis der Eclipse Refactoring sieht wie folgt aus und führt zu der Fehlermeldung unten, dass bezieht sich auf den Lesezugriff von entrySet in der loop Deklaration:

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> 

verändert I die Art der Erklärung der entrySet zu Set<Map.Entry<? extends K, ? extends V>>. Dieses Mal wird der Fehler bei der Initialisierung der Erklärung angegeben, nämlich:

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>> 

Da der ursprüngliche Code nicht kompiliert, ich bin ein wenig verwirrt. Vielleicht kann mir jemand helfen und eine Erklärung geben? Danke im Voraus!

+1

Beachten Sie, dass 'Set > entrySet = mapToCopy.entrySet(); 'wird funktionieren. [JLS § 14.4.2] (http://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.14.2) spricht über die Übersetzung von 'Iterable' in das Enhanced 'für' Aussage. Siehe auch [diese Antwort] (http://stackoverflow.com/a/16753901/5065475) –

+0

@AndyBrown: Können Sie bitte auf "Beachten Sie, dass [...] funktioniert", bitte. Was muss ich ändern, damit es funktioniert? – Marcus

+0

In Java8, versuchen Sie auch 'map.forEach ((Schlüssel, Wert) -> {...})'. Schlüssel/Wert werden als ein unbekannter Subtyp von K/V auf geeignete Typen zurückgeführt. Die API ist auch flexibel genug, wenn wir die Typen diktieren - 'map.forEach ((K-Taste, V-Wert) -> {...})' – ZhongYu

Antwort

0

Lets erste Rezension, die ursprüngliche Quelle:

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

Intern (und zur Laufzeit), wird diese kompiliert werden und funktionieren als:

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

wo ? extends K und ? extends V werden mit einigen ersetzt werden die echten Typen nach dem Typ löschen. Der Compiler wird wissen, um welche Art es sich handelt und wird keine Exception für Typinkompatibilität auslösen.

Auf der anderen Seite, wenn Sie die Quelle dieses Refactoring,

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) { 

    } 
} 

dann der Compiler keine Beweise haben, dass die entrySethält vom gleichen Typ wie Map.Entry<? extends K, ? extends V>, einfach weil die Platzhalter (?) steht immer für etwas unbekannt, dh es gibt keine Garantie, dass der Platzhalter aus dem entrySet Eintrag Schlüsselwert der Schlüsselwert entry (aus der Schleife) sein wird. Da der Compiler nicht hundertprozentig sicher ist, dass die Typen kompatibel sind, löst er einen Fehler bei der Kompilierung aus, ** obwohl Sie sicher sein können, dass diese Typen zur Laufzeit identisch sind.

+0

Vielen Dank für Ihre Antwort. Warum zeigt Ihr drittes Code-Snippet den angegebenen Fehler in der Schleifenanweisung? Ich kann das nicht reproduzieren. In meinem dritten Code-Snippet wird der Fehler in der Deklarationsanweisung angezeigt. – Marcus

+0

Ich denke, das Problem hängt nicht mit der For-Schleife zusammen. Ich habe die Frage ausgearbeitet und einen neuen Thread [hier] gestartet (http://stackoverflow.com/questions/32143844/type-mismatch-when-using-map-entryset). – Marcus

Verwandte Themen