У меня есть пара вопросов об общих подстановочных знаках в Java:
В чем разница между
List<? extends T>
иList<? super T>
?Что такое ограниченный шаблон и что такое неограниченный шаблон?
У меня есть пара вопросов об общих подстановочных знаках в Java:
В чем разница между List<? extends T>
и List<? super T>
?
Что такое ограниченный шаблон и что такое неограниченный шаблон?
Ответы:
В вашем первом вопросе <? extends T>
и <? super T>
приведены примеры ограниченных подстановочных знаков. Неограниченный подстановочный знак выглядит <?>
и в основном означает <? extends Object>
. Это примерно означает, что общий тип может быть любого типа. Ограниченный подстановочный знак ( <? extends T>
или <? super T>
) накладывает ограничение на тип, говоря, что он либо должен расширять определенный тип ( <? extends T>
известный как верхняя граница), либо должен быть предком определенного типа ( <? super T>
известен как нижняя граница) .
Учебные пособия по Java содержат довольно хорошее объяснение универсальных шаблонов в статьях « Подстановочные знаки» и « Еще больше удовольствия с подстановочными знаками» .
<? 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 - некоторый тип. То есть тип неизвестен, но на него можно поставить "привязку". В этом случае он ограничен некоторым классом, который является подклассом B.
List<? super B>
описывает, как List принимает тип, который является родительским классом класса B ? Вот почему C и D не компилируются?
У Джоша Блоха также есть хорошее объяснение того, когда использовать, super
и extends
в этом видео-разговоре google io он упоминает мнемонику Producer extends
Consumersuper
.
Из слайдов презентации:
Предположим, вы хотите добавить массовые методы в
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));
}
}
Общие подстановочные знаки созданы, чтобы сделать методы, работающие с Collection, более пригодными для повторного использования.
Например, если у метода есть параметр List<A>
, мы можем передать его только List<A>
этому методу. При некоторых обстоятельствах это бесполезно для работы этого метода :
List<A>
, тогда нам должно быть разрешено передавать List<A-sub>
этому методу. (Потому что A-sub ЯВЛЯЕТСЯ A)List<A>
, тогда нам должно быть разрешено передавать List<A-super>
этому методу. (Потому что А-супер)