Ich lese über generische Methoden von OracleDocGenericMethod . Ich bin ziemlich verwirrt über den Vergleich, wenn es darum geht, wann Platzhalter und wann generische Methoden verwendet werden sollen. Zitieren aus dem Dokument.
interface Collection<E> { public boolean containsAll(Collection<?> c); public boolean addAll(Collection<? extends E> c); }
Wir hätten hier stattdessen generische Methoden verwenden können:
interface Collection<E> { public <T> boolean containsAll(Collection<T> c); public <T extends E> boolean addAll(Collection<T> c); // Hey, type variables can have bounds too! }
[…] Dies sagt uns, dass das Typargument für den Polymorphismus verwendet wird; Die einzige Auswirkung besteht darin, dass verschiedene tatsächliche Argumenttypen an verschiedenen Aufrufstandorten verwendet werden können. In diesem Fall sollten Platzhalter verwendet werden. Platzhalter unterstützen die flexible Untertypisierung, die wir hier ausdrücken möchten.
Denken wir nicht, dass Wildcards wie (Collection<? extends E> c);
auch Polymorphismus unterstützen? Warum wird die Verwendung generischer Methoden dann als nicht gut angesehen?
Weiter geht es, heißt es,
Mit generischen Methoden können Typparameter verwendet werden, um Abhängigkeiten zwischen den Typen eines oder mehrerer Argumente für eine Methode und / oder ihren Rückgabetyp auszudrücken. Wenn es keine solche Abhängigkeit gibt, sollte keine generische Methode verwendet werden.
Was bedeutet das?
Sie haben das Beispiel vorgestellt
class Collections { public static <T> void copy(List<T> dest, List<? extends T> src) { ... }
[…]
Wir hätten die Signatur für diese Methode auch anders schreiben können, ohne Platzhalter zu verwenden:
class Collections { public static <T, S extends T> void copy(List<T> dest, List<S> src) { ... }
Das Dokument entmutigt die zweite Deklaration und fördert die Verwendung der ersten Syntax? Was ist der Unterschied zwischen der ersten und der zweiten Erklärung? Beide scheinen dasselbe zu tun?
Kann jemand Licht in diesen Bereich bringen.
?
überhaupt verwenden müssen. Sie können es als `public static <T1 erweitert Number, T2 erweitert Number> void copy (List <T1> dest, List <T2> src) umschreiben und in diesem Fall wird klar, was los ist.List
using-Typparameter definieren.List<T super Integer>
ist nicht gültig und wird nicht kompiliert.<T extends X & Y>
-> mehrere Grenzen.Betrachten Sie das folgende Beispiel aus der 4. Ausgabe von The Java Programming von James Gosling, in dem wir 2 SinglyLinkQueue zusammenführen möchten:
Beide oben genannten Methoden haben die gleiche Funktionalität. Was ist also vorzuziehen? Die Antwort ist die 2.. In den eigenen Worten des Autors:
"Die allgemeine Regel lautet, wenn möglich Platzhalter zu verwenden, da Code mit Platzhaltern im Allgemeinen besser lesbar ist als Code mit mehreren Typparametern. Wenn Sie entscheiden, ob Sie eine Typvariable benötigen, fragen Sie sich, ob diese Typvariable verwendet wird, um zwei oder mehr Parameter zu verknüpfen. oder um einen Parametertyp mit dem Rückgabetyp zu verknüpfen. Wenn die Antwort Nein lautet, sollte ein Platzhalter ausreichen. "
Hinweis: Im Buch wird nur die zweite Methode angegeben, und der Name des Typparameters lautet S anstelle von 'T'. Die erste Methode ist nicht im Buch enthalten.
quelle
In Ihrer ersten Frage: Wenn eine Beziehung zwischen dem Typ des Parameters und dem Rückgabetyp der Methode besteht, verwenden Sie ein generisches.
Beispielsweise:
Hier extrahieren Sie einige der T nach bestimmten Kriterien. Wenn T ist, werden
Long
Ihre Methoden zurückkehrenLong
undCollection<Long>
; Der tatsächliche Rückgabetyp hängt vom Parametertyp ab. Daher ist es nützlich und ratsam, generische Typen zu verwenden.Wenn dies nicht der Fall ist, können Sie Platzhaltertypen verwenden:
In diesen beiden Beispielen sind die Rückgabetypen unabhängig vom Typ der Elemente in den Sammlungen
int
undboolean
.In Ihren Beispielen:
Diese beiden Funktionen geben einen Booleschen Wert zurück, unabhängig von der Art der Elemente in den Sammlungen. Im zweiten Fall ist es auf Instanzen einer Unterklasse von E beschränkt.
Zweite Frage:
Mit diesem ersten Code können Sie einen heterogenen
List<? extends T> src
Parameter übergeben. Diese Liste kann mehrere Elemente verschiedener Klassen enthalten, solange sie alle die Basisklasse T erweitern.wenn du hättest:
und
du könntest es tun
Andererseits
beschränken Sie
List<S> src
sich auf eine bestimmte Klasse S, die eine Unterklasse von T ist. Die Liste kann nur Elemente einer Klasse (in diesem Fall S) und keiner anderen Klasse enthalten, selbst wenn sie auch T implementieren. Sie könnten mein vorheriges Beispiel nicht verwenden, aber Sie könnten:quelle
List<? extends Fruit> basket = new ArrayList<? extends Fruit>();
Ist keine gültige Syntax. Sie müssen die ArrayList ohne Grenzen instanziieren.ArrayList(Collection<? extends E> c)
. Können Sie mir erklären, warum Sie das gesagt haben?Die Wildcard-Methode ist ebenfalls generisch - Sie können sie mit einer Reihe von Typen aufrufen.
Die
<T>
Syntax definiert einen Typvariablennamen. Wenn eine Typvariable eine Verwendung hat (z. B. in der Methodenimplementierung oder als Einschränkung für einen anderen Typ), ist es sinnvoll, sie?
als anonyme Variable zu benennen, andernfalls könnten Sie sie verwenden . Sieht also nur nach einer Abkürzung aus.Darüber hinaus ist die
?
Syntax nicht vermeidbar, wenn Sie ein Feld deklarieren:quelle
Ich werde versuchen, Ihre Frage einzeln zu beantworten.
Nein. Der Grund dafür ist, dass der begrenzte Platzhalter keinen definierten Parametertyp hat. Es ist ein Unbekannter. Alles, was es "weiß", ist, dass das "Containment" von einem Typ ist
E
(was auch immer definiert ist). Daher kann nicht überprüft und begründet werden, ob der angegebene Wert mit dem begrenzten Typ übereinstimmt.Es ist also nicht sinnvoll, polymorphe Verhaltensweisen auf Platzhaltern zu haben.
Die erste Option ist in diesem Fall besser, da sie
T
immer begrenzt ist undsource
definitiv Werte (von Unbekannten) aufweist, die in Unterklassen unterteilt sindT
.Angenommen, Sie möchten alle Zahlenlisten kopieren. Die erste Option ist
src
Im wesentlichen kann annehmenList<Double>
,List<Float>
usw. , da es eine obere Grenze ist auf die parametrierte Typ gefundendest
.Die zweite Option zwingt Sie,
S
für jeden Typ, den Sie kopieren möchten, wie folgt zu bindenAs
S
ist ein parametrisierter Typ, der gebunden werden muss.Ich hoffe das hilft.
quelle
<S extends T>
Gibt an, dassS
es sich um einen parametrisierten Typ handelt, der eine Unterklasse vonT
ist. Daher ist ein parametrisierter Typ (kein Platzhalter) erforderlich, der eine Unterklasse von istT
.Ein weiterer Unterschied, der hier nicht aufgeführt ist.
Das Folgende führt jedoch zu einem Fehler bei der Kompilierung.
quelle
Soweit ich weiß, gibt es nur einen Anwendungsfall, in dem ein Platzhalter unbedingt benötigt wird (dh etwas ausdrücken kann, das Sie nicht mit expliziten Typparametern ausdrücken können). In diesem Fall müssen Sie eine Untergrenze angeben.
Abgesehen davon dienen Platzhalter jedoch dazu, präziseren Code zu schreiben, wie in den folgenden Anweisungen in dem von Ihnen erwähnten Dokument beschrieben:
quelle
Hauptsächlich -> Platzhalter erzwingen Generika auf Parameter- / Argumentebene einer nicht generischen Methode. Hinweis. Es kann standardmäßig auch in genericMethod ausgeführt werden, aber hier anstelle von? wir können T selbst verwenden.
Paket Generika;
SO Wildcard hat seine spezifischen Anwendungsfälle wie diesen.
quelle