Что означает вопросительный знак в параметре типа Java generics?

216

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

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

Я не беспокоюсь о деталях кода. Что меня смущает, так это то, что именно родовое выражение должно передавать на английском языке.

Может кто-то объяснить это мне?

sholsapp
источник

Ответы:

226
? extends HasWord

означает «класс / интерфейс, который расширяется HasWord». Другими словами, HasWordсам или любой из его детей ... в основном все, что будет работать с instanceof HasWordплюсом null.

В более технических терминах ? extends HasWordэто ограниченный подстановочный знак, описанный в пункте 31 « Эффективного Java 3-е издание» , начиная со страницы 139. Та же глава из 2-го издания доступна онлайн в формате PDF ; часть на ограниченных подстановочных знаках - это пункт 28, начиная со страницы 134.

Обновление: PDF-ссылка была обновлена, так как Oracle удалила ее некоторое время назад. Это теперь указывает на копию, размещенную в Университете королевы Марии Лондонской школы электронной инженерии и компьютерных наук.

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

Если вы объявляете метод, чья сигнатура ожидает от вас передачи List<HasWord>, то единственное, что вы можете передать, это a List<HasWord>.

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

Обратите внимание, что между List<? extends HasWord>и есть небольшая разница List<? super HasWord>. Как сказал Джошуа Блох: PECS = производитель-расширяет, потребитель-супер.

Это означает, что если вы передаете коллекцию, из которой ваш метод извлекает данные (т. Е. Коллекция создает элементы для вашего метода), вы должны использовать ее extends. Если вы передаете коллекцию, в которую ваш метод добавляет данные (т. Е. Коллекция использует элементы, которые создает ваш метод), его следует использовать super.

Это может показаться странным. Тем не менее, вы можете увидеть его в List«S sortкоманды (это просто ярлык версии два-Arg из Collections.sort). Вместо того, чтобы взять Comparator<T>, это на самом деле берет Comparator<? super T>. В этом случае Компаратор потребляет элементы для Listтого, чтобы изменить порядок самого Списка.

Powerlord
источник
17
«все, что будет работать с instanceof» - плюс нулевые значения. Помните, что нулевые значения возвращают false для любого экземпляра проверки.
Эяль Шнайдер
12
Не забывайте интерфейсы. ? не должен представлять класс!
Марк Питерс
9
List<HasWord>это именно то, что вы описываете: список, который содержит любые объекты, xдля которых x instanceof HasWordвозвращает true, и null. Вам не нужно подстановочный знак для этого. Подстановочный знак означает, что он также может быть списком другого типа, если этот тип является подтипом HasWord. (Извините за поздний комментарий.)
Pa
1
Ссылка в формате PDF, похоже, не работает, но это было полезно для меня: docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
user1338062
Это странно, я никогда не получал сообщения в прошлом году, когда @ user1338062 написал и не знал, что ссылка PDF не работает. Исправлено сейчас. Я также добавил ссылку на саму книгу.
Powerlord
65

Знак вопроса означает «любой тип». ?одни средства

Любой тип расширения Object(в том числе Object)

в то время как ваш пример выше означает

Любой тип, расширяющий или реализующий HasWord(в том числе, HasWordесли HasWordэто неабстрактный класс)

Jherico
источник
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 или B или их комбинацию, потому что оба класса расширяют одного и того же родителя или null(который не проверяет instanceof HasWorld).

LIMC
источник
8
Не только конкретные, но и абстрактные подклассы.
bloparod
2
Не только бетон и абстрактные подклассы, но и суб-интерфейсы: List<? extends Collection<String>> list = new ArrayList<List<String>>();.
Марк Питерс
2
Вам не нужны подстановочные знаки для этого, на самом деле: List<HasWord>так же хорошо для этого. Дело в том, что эта переменная может содержать List<A>или, List<B>а также List<HasWord>.
Пауло Эберманн
12

Возможно, придуманный пример из "реального мира" поможет.

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

(Примечание: ? extendsне означает «только для чтения». Например, я могу с надлежащими мерами предосторожности вынуть кусок мусора из мусорной корзины неизвестной специальности, а затем положить его в другое место.)

Не уверен, насколько это помогает. Указатель на указатель при наличии полиморфизма не совсем очевиден.

Том Хотин - Tackline
источник
5

По-английски:

Это Listнекоторый тип, который расширяет класс HasWord, в том числеHasWord

Вообще, ?в дженериках подразумевается любой класс. И extends SomeClassуказывает, что этот объект должен расширяться SomeClass(или быть этим классом).

jjnguy
источник
1
На самом деле, введите вместо класса . Это работает так же хорошо для интерфейсов.
Пауло Эберманн