Co oznacza znak zapytania w parametrze typu generics Java?

216

Jest to mały fragment kodu wzięty z niektórych przykładów towarzyszących parserowi Stanforda. Zajmuję się programowaniem w Javie od około 4 lat, ale nigdy nie bardzo dobrze rozumiałem, co powinien wskazywać ten styl kodu.

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

Nie martwię się o szczegóły kodu. To, co mnie dezorientuje, to dokładnie to, co powinno przekazać ogólne wyrażenie w języku angielskim.

Czy ktoś może mi to wyjaśnić?

sholsapp
źródło

Odpowiedzi:

226
? extends HasWord

oznacza „klasę / interfejs, który się rozszerza HasWord”. Innymi słowy, on HasWordsam lub którekolwiek z jego dzieci ... w zasadzie wszystko, co działałoby z instanceof HasWordplusem null.

Mówiąc bardziej technicznie, ? extends HasWordjest to symbol wieloznaczny ograniczony, objęty pozycją 31 Effective Java 3rd Edition , począwszy od strony 139. Ten sam rozdział z 2nd Edition jest dostępny online w formacie PDF ; część ograniczonych symboli wieloznacznych to pozycja 28, począwszy od strony 134.

Aktualizacja: link PDF został zaktualizowany, ponieważ Oracle usunął go jakiś czas temu. Teraz wskazuje na kopię hostowaną przez School of Electronic Engineering and Computer Science na Queen Mary University of London.

Aktualizacja 2: Przejdźmy trochę bardziej szczegółowo do tego, dlaczego chcesz używać symboli wieloznacznych.

Jeśli zadeklarujesz metodę, której podpis spodziewasz się przekazać List<HasWord>, jedyną rzeczą, którą możesz przekazać, jest List<HasWord>.

Jeśli jednak wspomniany podpis List<? extends HasWord>byłby, można List<ChildOfHasWord>zamiast tego przekazać .

Zauważ, że istnieje subtelna różnica między List<? extends HasWord>i List<? super HasWord>. Jak ujął to Joshua Bloch: PECS = producent rozszerza, konsument super.

Oznacza to, że jeśli przekazujesz kolekcję, z której twoja metoda pobiera dane (tj. Kolekcja wytwarza elementy do użycia przez metodę), powinieneś jej użyć extends. Jeśli przekazujesz kolekcję, do której twoja metoda dodaje dane (tj. Kolekcja zużywa elementy tworzone przez twoją metodę), należy jej użyć super.

Może to brzmieć myląco. Jednak można go zobaczyć w List„ssort polecenia (które jest po prostu skrót do wersji dwu-Arg z Collections.sort). Zamiast brać Comparator<T>, w rzeczywistości zajmuje Comparator<? super T>. W takim przypadku Komparator zużywa elementy List, aby zmienić kolejność samej listy.

Władca
źródło
17
„wszystko, co działałoby z instanceof” - plus wartości null. Pamiętaj, że wartości null zwracają wartość false dla każdego wystąpienia sprawdzania.
Eyal Schneider,
12
Nie zapomnij interfejsów. The? nie musi reprezentować klasy!
Mark Peters
9
List<HasWord>jest dokładnie tym, co opisujesz: lista zawierająca dowolne obiekty, xdla których x instanceof HasWordzwraca true, oraz null. Nie potrzebujesz do tego znaku wieloznacznego. Symbol wieloznaczny oznacza, że ​​tak naprawdę może to być również lista innego typu, o ile ten typ jest podtypem HasWord. (Przepraszam za późny komentarz.)
Paŭlo Ebermann
1
Wydaje się, że łącze PDF nie działa, ale przydało mi się to: docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
user1338062
To dziwne, nigdy nie dostałem wiadomości w zeszłym roku, kiedy @ user1338062 opublikował i nie wiedziałem, że link PDF nie działa. Naprawiono teraz. Dodałem również link do samej książki.
Władca Kwietnia
65

Znak zapytania oznacza „dowolny typ”. ?sam oznacza

Dowolny typ rozszerzający Object(w tym Object)

podczas gdy powyższy przykład oznacza

Dowolny typ rozszerzający lub implementujący HasWord(w tym HasWordjeśli HasWordjest klasą nieabstrakcyjną)

Jherico
źródło
2
co to public Set<Class<?>> getClasses()znaczy? Jaka jest różnica Set<Class>? Patrzę na javax.ws.rs.core.Application.
sklepienie
14

List<? extends HasWord>akceptuje wszelkie konkretne klasy, które rozszerzają HasWord. Jeśli masz następujące klasy ...

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

... wordListmoże zawierać TYLKO listę As lub Bs lub ich kombinację, ponieważ obie klasy rozszerzają tego samego rodzica lub null(co nie sprawdza instancji sprawdzania HasWorld).

limc
źródło
8
Nie tylko konkretne, ale także abstrakcyjne podklasy.
bloparod
2
Nie tylko konkretne i abstrakcyjne podklasy, ale również sub-interfejsy: List<? extends Collection<String>> list = new ArrayList<List<String>>();.
Mark Peters
2
Nie potrzebujesz do tego symboli wieloznacznych: w tym przypadku List<HasWord>równie dobrze. Chodzi o to, że ta zmienna może zawierać a List<A>lub List<B>a List<HasWord>.
Paŭlo Ebermann
12

Być może pomógłby wymyślony przykład „prawdziwego świata”.

W moim miejscu pracy mamy kosze na śmieci o różnych smakach. Wszystkie pojemniki zawierają śmieci, ale niektóre pojemniki są specjalistyczne i nie przyjmują wszystkich rodzajów śmieci. Więc mamy Bin<CupRubbish>i Bin<RecylcableRubbish>. System typów musi upewnić się, że nie mogę umieścić mojego HalfEatenSandwichRubbishw żadnym z tych typów, ale może przejść do ogólnego kosza na śmieci Bin<Rubbish>. Gdybym chciał porozmawiać o jednym Binz tych, Rubbishktóre mogą być wyspecjalizowane, więc nie mogę wrzucać niekompatybilnych śmieci, to byłoby to możliwe Bin<? extends Rubbish>.

(Uwaga: ? extendsnie oznacza tylko do odczytu. Na przykład mogę, przy zachowaniu odpowiednich środków ostrożności, wyjąć śmieci z kosza o nieznanej specjalizacji, a następnie umieścić je z powrotem w innym miejscu).

Nie jestem pewien, jak bardzo to pomaga. Wskaźnik do wskaźnika w obecności polimorfizmu nie jest całkowicie oczywisty.

Tom Hawtin - tackline
źródło
5

Po angielsku:

Jest to Listpewnego rodzaju rozszerzenie klasy HasWord, w tymHasWord

Ogólnie rzecz biorąc, ?rodzajowe oznacza dowolną klasę. I extends SomeClassokreśla, że ​​ten obiekt musi się rozszerzać SomeClass(lub być tą klasą).

jjnguy
źródło
1
W rzeczywistości wpisz zamiast klasy . Działa to równie dobrze w przypadku interfejsów.
Paŭlo Ebermann