Was bedeutet das Fragezeichen im Typparameter von Java Generics?

216

Dies ist ein kleiner Codeausschnitt aus einigen Beispielen, die dem Stanford Parser beiliegen. Ich habe in Java seit ungefähr 4 Jahren entwickelt, aber nie ein sehr starkes Verständnis dafür gehabt, was dieser Codestil anzeigen soll.

List<? extends HasWord> wordList = toke.tokenize();

Ich mache mir keine Sorgen um die Details des Codes. Was mich verwirrt, ist, was genau der generische Ausdruck auf Englisch vermitteln soll.

Kann mir das jemand erklären?

sholsapp
quelle

Antworten:

226
? extends HasWord

bedeutet "Eine Klasse / Schnittstelle, die sich erweitert HasWord." Mit anderen Worten, sich HasWordselbst oder eines seiner Kinder ... im Grunde alles, was mit instanceof HasWordPlus funktionieren würde null.

In technischer Hinsicht ? extends HasWordhandelt es sich um einen begrenzten Platzhalter, der in Punkt 31 der 3. Ausgabe von Effective Java ab Seite 139 behandelt wird. Dasselbe Kapitel aus der 2. Ausgabe ist online als PDF verfügbar . Der Teil auf begrenzten Platzhaltern ist Punkt 28 ab Seite 134.

Update: Der PDF-Link wurde aktualisiert, da Oracle ihn vor einiger Zeit entfernt hat. Es verweist nun auf die Kopie, die von der School of Electronic Engineering and Computer Science der Queen Mary University in London gehostet wird.

Update 2: Gehen wir etwas genauer darauf ein, warum Sie Platzhalter verwenden möchten.

Wenn Sie eine Methode deklarieren, deren Signatur erwartet, dass Sie übergeben List<HasWord>, können Sie nur a übergeben List<HasWord>.

Wenn diese Signatur jedoch vorhanden List<? extends HasWord>ist, können Sie List<ChildOfHasWord>stattdessen eine übergeben.

Beachten Sie, dass es einen subtilen Unterschied zwischen List<? extends HasWord>und gibt List<? super HasWord>. Wie Joshua Bloch es ausdrückte: PECS = Produzent-erweitert, Konsumenten-Super.

Dies bedeutet, dass Sie verwenden sollten, wenn Sie eine Sammlung übergeben, aus der Ihre Methode Daten abruft (dh die Sammlung produziert Elemente für Ihre Methode) extends. Wenn Sie eine Sammlung übergeben, zu der Ihre Methode Daten hinzufügt (dh die Sammlung verbraucht Elemente, die Ihre Methode erstellt), sollte sie verwendet werdensuper .

Das mag verwirrend klingen. Allerdings können Sie es sehen List‚s - sortBefehl (die nur eine Verknüpfung zu den beiden argument Version von Collections.sort ist). Anstatt a zu nehmen Comparator<T>, braucht es tatsächlich a Comparator<? super T>. In diesem Fall verbraucht der Komparator die Elemente von List, um die Liste selbst neu zu ordnen.

Powerlord
quelle
17
"alles, was mit instanceof funktionieren würde" - plus Nullwerte. Denken Sie daran, dass Nullwerte für jede Überprüfungsinstanz false zurückgeben.
Eyal Schneider
12
Schnittstellen nicht vergessen. Das ? muss keine Klasse darstellen!
Mark Peters
9
List<HasWord>ist genau das, was Sie beschreiben: Eine Liste, die alle Objekte enthält, xfür die x instanceof HasWordtrue zurückgegeben wird, und null. Sie benötigen dafür keinen Platzhalter. Der Platzhalter bedeutet, dass es sich tatsächlich auch um eine Liste eines anderen Typs handeln kann, sofern dieser Typ ein Subtyp von ist HasWord. (Entschuldigung für den späten Kommentar.)
Paŭlo Ebermann
1
PDF-Link scheint nicht zu funktionieren, aber das war nützlich für mich: docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
user1338062
Das ist komisch, ich habe letztes Jahr nie eine Nachricht erhalten, als @ user1338062 gepostet hat und nicht wusste, dass der PDF-Link nicht funktioniert. Jetzt behoben. Ich habe auch einen Link zum Buch selbst hinzugefügt.
Powerlord
65

Ein Fragezeichen ist ein Bezeichner für "jeden Typ". ?allein bedeutet

Jede Art von Erweiterung Object(einschließlich Object)

während Ihr Beispiel oben bedeutet

Jeder Typ, der erweitert oder implementiert wird HasWord(einschließlich HasWordif, wenn HasWordes sich um eine nicht abstrakte Klasse handelt)

Jherico
quelle
2
Was bedeutet public Set<Class<?>> getClasses()das? Was ist der Unterschied zu Set<Class>? Ich schaue javax.ws.rs.core.Application.
Gewölbe
14

List<? extends HasWord>akzeptiert alle konkreten Klassen, die HasWord erweitern. Wenn Sie die folgenden Klassen haben ...

public class A extends HasWord { .. }
public class B extends HasWord { .. }
public class C { .. }
public class D extends SomeOtherWord { .. }

... die wordListDose NUR eine Liste von entweder As oder Bs oder eine Mischung aus beiden enthalten kann, da beide Klassen dasselbe übergeordnete Element erweitern oder null(was die Instanz von Prüfungen nicht besteht HasWorld).

limc
quelle
8
Nicht nur konkrete, sondern auch abstrakte Unterklassen.
Bloparod
2
Nicht nur konkrete und abstrakte Unterklassen, sondern auch Unterschnittstellen : List<? extends Collection<String>> list = new ArrayList<List<String>>();.
Mark Peters
2
Dafür braucht man eigentlich List<HasWord>keine Platzhalter: tut das auch. Der Punkt hier ist, dass diese Variable ein List<A>oder List<B>sowie ein enthalten kann List<HasWord>.
Paŭlo Ebermann
12

Vielleicht würde ein erfundenes Beispiel aus der "realen Welt" helfen.

An meinem Arbeitsplatz haben wir Mülleimer, die in verschiedenen Geschmacksrichtungen erhältlich sind. Alle Mülleimer enthalten Müll, aber einige Mülleimer sind Spezialisten und nehmen nicht alle Arten von Müll auf. Also haben wir Bin<CupRubbish>und Bin<RecylcableRubbish>. Das Typsystem muss sicherstellen, dass ich meine nicht HalfEatenSandwichRubbishin einen dieser Typen einordnen kann, aber es kann in einen allgemeinen Mülleimer gelangen Bin<Rubbish>. Wenn ich über einen reden wollte Binvon Rubbishder spezialisiert werden kann , damit ich nicht in unvereinbar Müll setzen, dann das wäre Bin<? extends Rubbish>.

(Hinweis: ? extendsbedeutet nicht schreibgeschützt. Zum Beispiel kann ich mit den richtigen Vorsichtsmaßnahmen ein Stück Müll aus einem Behälter unbekannter Spezialität herausnehmen und später an einem anderen Ort zurückstellen.)

Ich bin mir nicht sicher, wie viel das hilft. Zeiger zu Zeiger bei Vorhandensein von Polymorphismus ist nicht ganz offensichtlich.

Tom Hawtin - Tackline
quelle
5

Auf Englisch:

Es ist ein ListTyp, der die Klasse erweitert HasWord, einschließlichHasWord

Im Allgemeinen bedeutet das ?in Generika jede Klasse. Und das extends SomeClassgibt an, dass dieses Objekt erweitert werden muss SomeClass(oder diese Klasse sein muss).

jjnguy
quelle
1
Eigentlich gibt statt Klasse . Dies funktioniert genauso gut für Schnittstellen.
Paŭlo Ebermann