Für eine generische Schnittstelle:
%Vor%Der Unterschied zwischen den beiden Feldern:
%Vor% Ist das foo2
ein generischer Typ und foo
nicht? Da ?
ein Platzhalter ist (was ich denke, jeden Typ) und jeder Typ ist ein Untertyp von Object, dann würde ich Foo<?>
und Foo<Object>
semantisch und syntaktisch gleichwertig erwarten.
Beachten Sie jedoch Folgendes:
%Vor% So Foo<?>
und Foo<Object>
sind nicht gleich syntaktisch.
Was ist hier los? Ich bin ziemlich fest daran, das zu verstehen.
Foo<?>
ist semantisch das gleiche wie Foo<? extends Object>
: es ist ein Foo
mit type Parameter von etwas Spezifischem, aber das einzige, was über "etwas" bekannt ist, ist, dass es eine Unterklasse von Object
ist (was isn sag nicht zu viel, da alle Klassen Unterklassen von Object
sind). Foo<Object>
hingegen ist ein Foo
mit dem Typparameter Object
. Während alles mit Object
zuweisungskompatibel ist, wird nicht alles mit ?
kompatibel sein, wobei ?
erweitert Object
ist.
Hier ist ein Beispiel dafür, warum Foo<?>
einen Fehler erzeugen sollte:
Ändern Sie jetzt Ihr Beispiel zu diesem:
%Vor% Da i
ein Integer
ist, kann der Compiler foo1.foo(i)
nicht kompilieren.
Beachten Sie, dass
%Vor% wird auch nicht gemäß den Regeln für passende parametrisierte Typen da Object
und String
nachweisbar unterschiedliche Typen sind.
Foo
(ohne Typ-Parameter überhaupt - ein roher Typ) sollte normalerweise als Programmierfehler betrachtet werden. Laut der Java-Sprachspezifikation (§4.8) ist der Der Compiler akzeptiert solchen Code, um nicht-generischen, alten Code nicht zu brechen.
Wegen Löschvorgang gibt es keinen Unterschied, wenn das Byte generiert wird Code. Das heißt, die einzigen Unterschiede zwischen diesen sind zum Zeitpunkt der Kompilierung.
Nun, aufgrund der Typ-Löschung ist alles, was mit Generika zu tun hat, nur Kompilierzeit; Ich nehme an, dass Sie das syntaktisch nennen.
Ich denke, Ihre anfängliche Einschätzung ist korrekt und der wirkliche Unterschied ist, dass sie eine generische typisierte Variable ist und das normale Foo nicht.
Der Platzhalter in Foo<?>
zeigt an, dass Sie im aktuellen Bereich nicht wissen oder sich nicht darum kümmern, welche Art von 'Foo' Sie haben.
Foo<?>
und Foo<? extends Object>
sind gleich (die erste ist eine Abkürzung für die andere). Foo<Object>
ist anders.
Ein konkretes Beispiel :
Sie können List
zu List<?>
zuweisen
z.B.
Wenn Sie List<?>
haben, können Sie size()
aufrufen, weil Sie nicht wissen müssen, um welche Art von Liste es sich handelt. Und Sie können get(i)
aufrufen, weil wir wissen, dass die Liste eine Art Object
enthält, so dass der Compiler sie so behandelt, als ob get
zurückgibt und Object
.
Aber Sie können add(o)
nicht aufrufen, weil Sie nicht wissen (und der Compiler weiß nicht), mit welcher Art von Liste Sie es zu tun haben.
In unserem obigen Beispiel möchten Sie list1.add(new Object());
nicht zulassen, da dies eine Liste von String
s
Der Grund für Platzhalter ist, dass Sie so etwas tun können:
%Vor% Dieser Code kann auf jede gewünschte Liste angewendet werden, zB List<String>
, List<Object>
, List<Integer>
usw.
Wenn die Signatur public static boolean containsNull(List<Object> list)
war, dann konnten Sie List<Object>
nur an sie übergeben, List<String>
funktionierte nicht.
Betrachten Sie diese Typen:
List<Object>
List<CharSequence>
List<String>
Auch wenn String
ein Untertyp von CharSequence
ist, der ein Untertyp von Object
ist, haben diese Listentypen keine Subtyp-Supertype-Beziehungen. (Seltsamerweise ist String[]
ein Untertyp von CharSequence[]
, der ein Untertyp von Object[]
ist, aber das ist aus historischen Gründen.)
Angenommen, wir wollen eine Methode schreiben, die eine Liste ausgibt. Wenn wir es tun
%Vor% Dies kann nicht List<String>
(ohne Hacks) drucken, da ein List<String>
kein List<Object>
ist. Aber mit Platzhaltern können wir
und übergeben Sie eine beliebige Liste.
Platzhalter können obere und untere Grenzen für zusätzliche Flexibilität haben. Angenommen, wir möchten eine Liste drucken, die nur CharSequence
s enthält. Wenn wir es tun
Dann stoßen wir auf das gleiche Problem - wir können nur ein List<CharSequence>
übergeben und unser List<String>
ist kein List<CharSequence>
. Aber wenn wir stattdessen
Dann können wir eine List<String>
und eine List<StringBuilder>
übergeben usw.