Що означає знак питання в параметрі типу дженерики Java?


216

Це невеликий фрагмент коду, взятий із деяких прикладів, які супроводжують Stanford Parser. Я розвивався в Java близько 4 років, але ніколи не мав дуже сильного розуміння того, що повинен вказувати цей стиль коду.

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

Я не переживаю за деталі коду. Мене плутає те, що саме має висловити родовий вираз англійською мовою.

Може хтось мені це пояснить?


Відповіді:


226
? extends HasWord

означає "Клас / інтерфейс, який розширюється HasWord." Іншими словами, HasWordсама або будь-яка її дитина ... в основному все, що працювало б з instanceof HasWordплюсом null.

Більш технічно, ? extends HasWordце обмежений підстановочний код, який розглядається в пункті 31 Ефективної Java 3-го видання , починаючи з сторінки 139. Цей самий розділ з другого видання доступний в Інтернеті як PDF ; Частина на обмежених символах - пункт 28, починаючи зі сторінки 134.

Оновлення: посилання PDF було оновлено, оскільки Oracle деякий час його видалив. Тепер він вказує на копію, яку розмістила Лондонська школа електронної інженерії та комп’ютерних наук з університету королеви Мері.

Оновлення 2: Давайте детальніше розберемося, чому ви хочете використовувати подстановочні символи.

Якщо ви заявляєте метод, підпис якого очікує, що ви перейдете List<HasWord>, то єдине, що ви можете передати - це List<HasWord>.

Однак, якщо згаданий підпис був List<? extends HasWord>тоді, ви можете List<ChildOfHasWord>замість цього передати .

Зауважте, що між List<? extends HasWord>і та є незначна різниця List<? super HasWord>. Як сказав Джошуа Блох: PECS = виробник розширює, споживач - супер.

Це означає, що якщо ви передаєте колекцію, з якої ваш метод витягує дані (тобто колекція створює елементи для використання вашого методу), ви повинні використовувати extends. Якщо ви передаєте колекцію, до якої ваш метод додає дані (тобто колекція споживає елементи, створені вашим методом), він повинен використовувати super.

Це може здатися заплутаним. Однак ви можете побачити його в команді List's sort(що є лише ярликом до двоаргументованої версії Collections.sort). Замість того, щоб взяти a Comparator<T>, він насправді бере a Comparator<? super T>. У цьому випадку компаратор споживає елементи Listсимволу для того, щоб впорядкувати сам Список.


17
"все, що буде працювати з instanceof" - плюс нульові значення. Пам'ятайте, що нульові значення повертають помилкові для будь-якого instanceof перевірки.
Eyal Schneider

12
Не забувайте інтерфейси. ? не повинен представляти клас!
Марк Петерс

9
List<HasWord>саме те, що ви описуєте: Список, який містить будь-які об'єкти, xдля яких x instanceof HasWordповертає істину, і null. Для цього вам не потрібна маска. Підстановочний знак означає, що він також може бути списком іншого типу, якщо цей тип є підтипом HasWord. (Вибачте за пізній коментар.)
Paŭlo Ebermann,

1
PDF-посилання, здається, не працює, але мені це було корисно: docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
user1338062

Це дивно, я ніколи не отримував повідомлення минулого року, коли @ user1338062 опублікував і не знав, що посилання PDF не працює. Виправлено зараз. Я також додав посилання на саму книгу.
Powerlord

65

Знак питання - це означення "будь-якого типу". ?поодинці означає

Будь-який тип, що розширюється Object(включаючи Object)

тоді як ваш приклад вище означає

Будь-який тип, що розширює або впроваджує HasWord(включаючи, HasWordякщо HasWordце не абстрактний клас)


2
так що public Set<Class<?>> getClasses()означає? У чому різниця Set<Class>? Я дивлюсь javax.ws.rs.core.Application.
склепіння

14

List<? extends HasWord>приймає будь-які конкретні класи, які розширюють HasWord. Якщо у вас є такі класи ...

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

... wordListможе САМО містити список або As, або Bs, або суміш обох, тому що обидва класи поширюють один і той же батьківський null( або не вдається instanceof перевірити HasWorld).


8
Не тільки конкретні, але й абстрактні підкласи.
bloparod

2
Не тільки бетон і абстрактні підкласи, а й суб-інтерфейси: List<? extends Collection<String>> list = new ArrayList<List<String>>();.
Марк Петерс

2
Насправді для цього вам не потрібні символи підстановки: List<HasWord>так само добре для цього. Суть у тому, що ця змінна може містити як List<A>або, List<B>так і а List<HasWord>.
Paŭlo Ebermann

12

Можливо, допоможе надуманий приклад "реального світу".

На моєму місці роботи у нас є контейнери для сміття, які виходять різними смаками. Усі сміттєві контейнери містять сміття, але деякі контейнери є спеціалізованими і не приймають усі види сміття. Так у нас Bin<CupRubbish>і є Bin<RecylcableRubbish>. Система типу повинна переконатися, що я не можу поставити HalfEatenSandwichRubbishжоден із цих типів, але вона може перейти у загальний сміттєвий контейнер Bin<Rubbish>. Якби я хотів поговорити про Binз Rubbishяких може бути спеціалізовані , тому я не можу поставити в суперечливому смітті, то це було б Bin<? extends Rubbish>.

(Примітка: ? extendsне означає лише для читання. Наприклад, я можу з належними запобіжними засобами вийняти шматок сміття з бака невідомої спеціальності та пізніше перенести його в інше місце.)

Не впевнений, наскільки це допомагає. Покажчик-покажчик у присутності поліморфізму не зовсім очевидний.


5

Англійською:

Це Listпевний тип, який розширює клас HasWord, в тому числіHasWord

Взагалі ?в "generics" означає будь-який клас. І extends SomeClassвказує, що цей об'єкт повинен поширюватися SomeClass(або бути цим класом).


1
Насправді введіть замість класу . Це працює так само добре для інтерфейсів.
Paŭlo Ebermann

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.