Це залежить від того, що вам потрібно зробити. Вам потрібно використовувати параметр обмеженого типу, якщо ви хотіли зробити щось подібне:
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>
та а Circle
).
Отже, надаючи ім’я параметру обмеженого типу, ми маємо можливість використовувати його в іншому місці нашого загального методу. Звичайно, ця інформація потрібна не завжди, тому, якщо вам не потрібно знати стільки про тип (наприклад, ваш drawAll
), тоді достатньо лише символів підстановки.
Навіть якщо ви знову не звертаєтеся до параметра обмеженого типу, параметр обмеженого типу все одно необхідний, якщо у вас кілька меж. Ось цитата із поширених запитань про Java Generics Анжеліки Лангер
У чому різниця між зв’язаними символами підстановки та параметрами типу?
Узагальнюючий знак може мати лише одну межу, тоді як параметр типу може мати кілька меж. Узагальнюючий знак може мати нижню або верхню межу, тоді як для параметра типу не існує поняття нижньої межі.
Межі підстановки та типи параметрів часто плутають, оскільки вони обидва називаються межами і мають частково подібний синтаксис. […]
Синтаксис :
type parameter bound T extends Class & Interface1 & … & InterfaceN
wildcard bound
upper bound ? extends SuperType
lower bound ? super SubType
Узагальнюючий знак може мати лише одну межу, або нижню, або верхню. Список обмежених знаків не допускається.
Параметр типу, у constrast, може мати кілька меж, але для поняття типу не існує такого поняття, як нижня межа.
Цитати з Effective Java 2nd Edition, пункт 28: Використовуйте обмежені символи підстановки для збільшення гнучкості API :
Для максимальної гнучкості використовуйте типи підстановок на вхідних параметрах, що представляють виробників або споживачів. […] PECS означає «виробник» extends
, «споживач» super
[…]
Не використовуйте типи підстановок як типи повернення . Замість того, щоб забезпечити додаткову гнучкість для ваших користувачів, це змусить їх використовувати підстановочні типи в коді клієнта. При правильному використанні типи підстановок майже не видно для користувачів класу. Вони змушують методи приймати параметри, які вони повинні прийняти, та відкидати ті, які вони повинні відкидати. Якщо користувачеві класу доводиться думати про типи підстановок, можливо, щось не так з API класу .
Застосовуючи принцип PECS, тепер ми можемо повернутися до нашого addIfPretty
прикладу та зробити його більш гнучким, написавши наступне:
public <T extends Shape> void addIfPretty(List<? super T> list, T shape) { … }
Тепер ми можемо addIfPretty
, скажімо, a Circle
, a List<Object>
. Це, очевидно, безпечно для типу, і все ж наша оригінальна декларація була недостатньо гнучкою, щоб дозволити це.
Пов’язані запитання
Резюме
- Використовуйте обмежені параметри типу / узагальнюючі символи, вони збільшують гнучкість вашого API
- Якщо тип вимагає декількох параметрів, вам не залишається нічого іншого, як використовувати параметр обмеженого типу
- якщо тип вимагає нижньої межі, вам не залишається нічого іншого, як використовувати обмежений підстановочний знак
- "Виробники" мають нижчі межі, "споживачі" - нижчі
- Не використовуйте символи підстановки у типах повернення