У мене є кілька запитань щодо загальних підстановок на Яві:
Яка різниця між
List<? extends T>
іList<? super T>
?Що таке обмежений підстановочний код і що таке необмежений підстановочний знак?
У мене є кілька запитань щодо загальних підстановок на Яві:
Яка різниця між List<? extends T>
іList<? super T>
?
Що таке обмежений підстановочний код і що таке необмежений підстановочний знак?
Відповіді:
У першому питанні, <? extends T>
і <? super T>
є прикладами обмежених групових символів. Необов’язаний підстановочний вигляд виглядає <?>
, і в основному означає <? extends Object>
. Це вільно означає, що родова може бути будь-якого типу. Обмежений підстановочний знак ( <? extends T>
або <? super T>
) встановлює обмеження на тип, кажучи, що він або повинен поширювати певний тип ( <? extends T>
відомий як верхня межа), або повинен бути родоначальником певного типу ( <? super T>
відомий як нижня межа) .
Навчальні програми Java містять досить непогані пояснення щодо дженериків у статтях Wildcards та More Fun with Wildcards .
<? super C>
означає, що ваш тип обмежений чимось вище C
в ієрархії типів. (Вибачте за надзвичайно пізню відповідь. Гадаю, у нас не було сповіщень про коментарі 2 роки тому?)
Якщо у вас є ієрархія класів A, B - це підклас A, а C і D - це підклас B, як показано нижче
class A {}
class B extends A {}
class C extends B {}
class D extends B {}
Тоді
List<? extends A> la;
la = new ArrayList<B>();
la = new ArrayList<C>();
la = new ArrayList<D>();
List<? super B> lb;
lb = new ArrayList<A>(); //fine
lb = new ArrayList<C>(); //will not compile
public void someMethod(List<? extends B> lb) {
B b = lb.get(0); // is fine
lb.add(new C()); //will not compile as we do not know the type of the list, only that it is bounded above by B
}
public void otherMethod(List<? super B> lb) {
B b = lb.get(0); // will not compile as we do not know whether the list is of type B, it may be a List<A> and only contain instances of A
lb.add(new B()); // is fine, as we know that it will be a super type of A
}
Обмежений підстановочний знак - це те, ? extends B
де B - якийсь тип. Тобто тип невідомий, але на ньому може бути розміщена "прив'язка". У цьому випадку він обмежений деяким класом, який є підкласом В.
List<? super B>
описує, як List приймає тип, який є батьківським класом класу B ? Чому C і D не збирають hmm?
Джош Блох також має гарне пояснення, коли його використовувати, super
і extends
в цій відео-розмові Google про io, де він згадує про мнемонічний виробник extends
споживачівsuper
.
З слайдів презентації:
Припустимо, ви хочете додати масові методи
Stack<E>
void pushAll(Collection<? extends E> src);
- src - виробник E
void popAll(Collection<? super E> dst);
- dst - споживач E
Можуть бути випадки, коли вам потрібно обмежити типи типів, дозволених для передачі параметру типу. Наприклад, метод, який працює на числах, може захотіти приймати лише екземпляри Number або його підкласи. Для цього призначені параметри обмеженого типу.
Collection<? extends MyObject>
означає, що він може приймати всіх об'єктів, які мають IS-A зв'язок з MyObject (тобто будь-який об'єкт, який є типом myObject, або ми можемо сказати будь-який об'єкт будь-якого підкласу MyObject) або об'єкт класу MyObject.
Наприклад:
class MyObject {}
class YourObject extends MyObject{}
class OurObject extends MyObject{}
Тоді,
Collection<? extends MyObject> myObject;
буде приймати лише MyObject або дітей MyObject (тобто будь-який об’єкт типу OurObject або YourObject або MyObject, але не будь-який об'єкт надкласу MyObject).
В загальному,
Якщо структура містить елементи з типом форми
? extends E
, ми можемо отримати елементи зі структури, але ми не можемо вкласти елементи в структуру
List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3.14); // compile-time error
assert ints.toString().equals("[1, 2, 3.14]");
Щоб помістити елементи в структуру, нам потрібен інший вид підстановки під назвою Wildcards with super
,
List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
List<Integer> ints = Arrays.asList(5, 6);
Collections.copy(objs, ints);
assert objs.toString().equals("[5, 6, four]");
public static <T> void copy(List<? super T> dst, List<? extends T> src) {
for (int i = 0; i < src.size(); i++) {
dst.set(i, src.get(i));
}
}
Загальні підстановки створені для того, щоб зробити методи, які працюють на колекції, більше використані.
Наприклад, якщо метод має параметр List<A>
, ми можемо надати лише List<A>
цей метод. Даремно цей спосіб функціонує за певних обставин:
List<A>
, тоді нам слід дозволити надати List<A-sub>
цьому методу. (Тому що A-sub IS A A)List<A>
, то нам слід дозволити надати List<A-super>
цьому методу. (Тому що А - супер)