In diesem Beispiel:
import java.util.*;
public class Example {
static void doesntCompile(Map<Integer, List<? extends Number>> map) {}
static <T extends Number> void compiles(Map<Integer, List<T>> map) {}
static void function(List<? extends Number> outer)
{
doesntCompile(new HashMap<Integer, List<Integer>>());
compiles(new HashMap<Integer, List<Integer>>());
}
}
doesntCompile()
Kompiliert nicht mit:
Example.java:9: error: incompatible types: HashMap<Integer,List<Integer>> cannot be converted to Map<Integer,List<? extends Number>>
doesntCompile(new HashMap<Integer, List<Integer>>());
^
while compiles()
wird vom Compiler akzeptiert.
Diese Antwort erklärt , dass der einzige Unterschied ist , dass im Gegensatz zu <? ...>
, <T ...>
können Sie die Art Referenz später, das scheint nicht der Fall zu sein.
Was ist der Unterschied zwischen <? extends Number>
und <T extends Number>
in diesem Fall und warum wird die erste Kompilierung nicht durchgeführt?
Antworten:
Durch Definieren der Methode mit der folgenden Signatur:
und aufrufen wie:
In jls §8.1.2 finden wir, dass (interessanter Teil von mir fett gedruckt):
Mit anderen Worten, der Typ
T
wird mit dem Eingabetyp abgeglichen und zugewiesenInteger
. Die Signatur wird effektivstatic void compiles(Map<Integer, List<Integer>> map)
.Wenn es um
doesntCompile
Methoden geht, definiert jls Regeln für die Untertypisierung ( §4.5.1 , von mir fett gedruckt):Dies bedeutet, dass
? extends Number
tatsächlich enthältInteger
oder sogarList<? extends Number>
enthältList<Integer>
, aber es ist nicht der Fall fürMap<Integer, List<? extends Number>>
undMap<Integer, List<Integer>>
. Mehr zu diesem Thema finden Sie in diesem SO-Thread . Sie können die Version mit?
Platzhalter weiterhin zum Laufen bringen, indem Sie deklarieren, dass Sie einen Subtyp vonList<? extends Number>
:quelle
? extends Number
eher als? extends Numeric
. [2] Ihre Behauptung, dass "dies bei List <? Extend Number> und List <Integer> nicht der Fall ist ", ist falsch. Wie @VinceEmigh bereits betont hat, können Sie eine Methode erstellenstatic void demo(List<? extends Number> lst) { }
und sodemo(new ArrayList<Integer>());
oder so aufrufendemo(new ArrayList<Float>());
, und der Code wird kompiliert und in Ordnung ausgeführt. Oder verstehe ich vielleicht falsch oder verstehe ich falsch, was Sie gesagt haben?List<? extends Number>
als Typparameter die gesamte Karte, nicht sich selbst. Vielen Dank für den Kommentar.List<Number>
enthält nichtList<Integer>
. Angenommen, Sie haben eine Funktionstatic void check(List<Number> numbers) {}
. Wenn der Aufrufcheck(new ArrayList<Integer>());
damit nicht kompiliert wird, müssen Sie die Methode als definierenstatic void check(List<? extends Number> numbers) {}
. Mit der Karte ist es das gleiche, aber mit mehr Verschachtelung.Number
ein Typparameter der Liste und Sie müssen hinzufügen? extends
, um sie kovariant zu machen,List<? extends Number>
ist ein Typparameter vonMap
und benötigt auch? extends
Kovarianz.Im Anruf:
T ist mit Integer abgeglichen, daher ist der Typ des Arguments a
Map<Integer,List<Integer>>
. Dies ist bei der Methode nicht der FalldoesntCompile
: Der Typ des Arguments bleibtMap<Integer, List<? extends Number>>
unabhängig vom tatsächlichen Argument im Aufruf. und das ist nicht abtretbar vonHashMap<Integer, List<Integer>>
.AKTUALISIEREN
In der
doesntCompile
Methode hindert Sie nichts daran, Folgendes zu tun:Es kann also offensichtlich kein
HashMap<Integer, List<Integer>>
als Argument akzeptieren .quelle
doesntCompile
dann aussehen? Nur neugierig darauf.doesntCompile(new HashMap<Integer, List<? extends Number>>());
würde genauso funktionieren wiedoesntCompile(new HashMap<>());
.HashMap<Integer, List<Integer>>
" Könnten Sie bitte erläutern, warum es nicht von ihm zuweisbar ist?Vereinfachtes Demonstrationsbeispiel. Das gleiche Beispiel kann wie unten dargestellt werden.
List<Pair<? extends Number>>
ist ein mehrstufiger Platzhaltertyp, währendList<? extends Number>
es sich um einen Standard-Platzhaltertyp handelt.Gültige konkrete Instanziierungen des Platzhaltertyps
List<? extends Number>
umfassenNumber
und alle Untertypen von,Number
während in diesem FallList<Pair<? extends Number>>
ein Typargument vom Typargument ist und selbst eine konkrete Instanziierung des generischen Typs aufweist.Generika sind unveränderlich, sodass
Pair<? extends Number>
Wildcard-Typen nur akzeptiert werden könnenPair<? extends Number>>
. Der innere Typ? extends Number
ist bereits kovariant. Sie müssen den umschließenden Typ als Kovariante festlegen, um die Kovarianz zuzulassen.quelle
<Pair<Integer>>
es nicht funktioniert<Pair<? extends Number>>
, aber funktioniert<T extends Number> <Pair<T>>
?T
gegen zu verstehen?
. Ein Teil des Problems besteht darin, dass Andronicus, wenn er den entscheidenden Punkt seiner Erklärung erreicht, auf einen anderen Thread zurückgreift, der nur triviale Beispiele verwendet. Ich hatte gehofft, hier eine klarere und vollständigere Antwort zu bekommen.Ich würde Ihnen empfehlen, in der Dokumentation allgemeiner Platzhalter nachzuschlagen, insbesondere in Richtlinien für die Verwendung von Platzhaltern
Ehrlich gesagt Ihre Methode #doesntCompile
und wie anrufen
Ist grundsätzlich falsch
Fügen wir die rechtliche Umsetzung hinzu:
Es ist wirklich in Ordnung, weil Double Number erweitert, also ist Put
List<Double>
absolut in OrdnungList<Integer>
, oder?Nehmen Sie jedoch immer noch an, dass es legal ist , hier
new HashMap<Integer, List<Integer>>()
von Ihrem Beispiel abzuweichen?Der Compiler glaubt das nicht und tut sein Bestes, um solche Situationen zu vermeiden.
Versuchen Sie, die gleiche Implementierung mit der Methode #compile durchzuführen, und der Compiler erlaubt Ihnen offensichtlich nicht, eine Liste von Doubles in die Map einzufügen.
Grundsätzlich können Sie nichts setzen, aber
List<T>
deshalb ist es sicher, diese Methode mitnew HashMap<Integer, List<Integer>>()
odernew HashMap<Integer, List<Double>>()
odernew HashMap<Integer, List<Long>>()
oder aufzurufennew HashMap<Integer, List<Number>>()
.Kurz gesagt, Sie versuchen, mit dem Compiler zu schummeln, und dieser verteidigt sich ziemlich gegen solchen Schummel.
NB: Die Antwort von Maurice Perry ist absolut richtig. Ich bin mir nur nicht sicher, ob es klar genug ist, also habe ich versucht (ich hoffe wirklich, dass ich es geschafft habe), einen umfangreicheren Beitrag hinzuzufügen.
quelle