Ответ 1
Это зависит от того, что вам нужно делать. Вам нужно использовать параметр ограниченного типа, если вы хотите сделать что-то вроде этого:
public <T extends Shape> void addIfPretty(List<T> shapes, T shape) {
if (shape.isPretty()) {
shapes.add(shape);
}
}
Здесь мы имеем a List<T> shapes
и a T shape
, поэтому можем безопасно shapes.add(shape)
. Если он был объявлен List<? extends Shape>
, вы не можете безопасно add
к нему (потому что у вас могут быть List<Square>
и a Circle
).
Таким образом, задавая имя параметру ограниченного типа, мы можем использовать его в другом месте в нашем общем методе. Разумеется, эта информация не всегда требуется, поэтому, если вам не нужно много знать о типе (например, ваш drawAll
), тогда достаточно всего лишь подстановочного знака.
Даже если вы снова не ссылаетесь на параметр ограниченного типа, параметр ограниченного типа по-прежнему требуется, если у вас есть несколько ограничений. Здесь цитата из Часто задаваемые вопросы по Java Generics Angelika Langer
В чем разница между привязкой шаблона и привязкой к параметру типа?
Подстановочный знак может иметь только одну границу, в то время как параметр типа может иметь несколько границ. Подстановочный знак может иметь нижнюю или верхнюю границу, в то время как для параметра типа нет нижней границы.
Ограничения подстановочных знаков и ограничения параметров типа часто сбиты с толку, потому что оба они называются границами и имеют аналогичный синтаксис. [...]
Синтаксис
type parameter bound T extends Class & Interface1 & … & InterfaceN wildcard bound upper bound ? extends SuperType lower bound ? super SubType
Подстановочный знак может иметь только одну границу, либо нижнюю, либо верхнюю границу. Список подстановочных ограничений не разрешен.
Параметр типа в constrast может иметь несколько границ, но нет такой вещи, как нижняя граница для параметра типа.
Цитаты из Effective Java 2nd Edition, Item 28: Используйте ограниченные подстановочные знаки для повышения гибкости API:
Для максимальной гибкости используйте типы подстановок для входных параметров, которые представляют производителей или потребителей. [...] PECS обозначает производителя < <2 → , потребитель-
super
[...]Не используйте типы подстановок в качестве типов возврата. Вместо того, чтобы предоставлять дополнительную гибкость для ваших пользователей, это заставит их использовать подстановочные типы в клиентском коде. Правильно используемые типы подстановочных знаков почти невидимы для пользователей класса. Они вызывают методы принятия параметров, которые они должны принимать, и отвергают те, которые они должны отклонить. Если пользователь класса должен думать о типах подстановочных знаков, возможно, что-то не так с API класса.
Применяя принцип PECS, мы можем вернуться к нашему примеру addIfPretty
и сделать его более гибким, написав следующее:
public <T extends Shape> void addIfPretty(List<? super T> list, T shape) { … }
Теперь мы можем addIfPretty
, скажем, a Circle
, на a List<Object>
. Это явно типично, но наша оригинальная декларация не была достаточно гибкой, чтобы позволить ей.
Связанные вопросы
- Java Generics: Что такое PECS?
- Может ли кто-нибудь объяснить, что означает
<? super T>
, и когда он должен использоваться и как эта конструкция должна взаимодействовать с<T>
и<? extends T>
?
Резюме
- Использовать параметры ограниченного типа/подстановочные знаки, они повышают гибкость вашего API.
- Если тип требует нескольких параметров, у вас нет выбора, кроме как использовать параметр ограниченного типа
- Если тип требует нижнего, у вас нет выбора, кроме как использовать ограниченный шаблон
- "Продюсеры" имеют верхние границы, "потребители" имеют нижние границы.
- Не используйте подстановочные знаки в обратных типах