Дженерики Java (подстановочные знаки)

У меня есть пара вопросов об общих подстановочных знаках в Java:

  1. В чем разница между List<? extends T> и List<? super T>?

  2. Что такое ограниченный подстановочный знак и что такое неограниченный подстановочный знак?


person Pablo Fernandez    schedule 30.10.2008    source источник
comment
В случае, если Тед Гао ответ удален (так как он был только для ссылки), вот сообщение в блоге, на которое есть ссылка.   -  person royhowie    schedule 25.04.2015


Ответы (6)


В вашем первом вопросе <? extends T> и <? super T> являются примерами ограниченных подстановочных знаков. Неограниченный подстановочный знак выглядит как <?> и в основном означает <? extends Object>. Это примерно означает, что общий тип может быть любого типа. Ограниченный подстановочный знак (<? extends T> или <? super T>) накладывает ограничение на тип, говоря, что он должен либо расширять определенный тип (<? extends T> известен как верхняя граница), либо должен быть предком конкретный тип (<? super T> известен как нижняя граница).

В руководствах по Java есть довольно хорошие объяснения универсальных шаблонов в статьях Подстановочные знаки и Больше удовольствия с подстановочными знаками.

person Bill the Lizard    schedule 30.10.2008
comment
Просто чтобы понять, если A ‹B и B‹ C, то: ‹A extends C› неверно? - person Pablo Fernandez; 31.10.2008
comment
Если под A ‹B вы имеете в виду, что A расширяет B, тогда A расширяет C. Однако вы бы не использовали это в синтаксисе с подстановочными знаками, вы бы сказали‹? расширяет C ›, чтобы ограничить ваш выбор A или B. - person Bill the Lizard; 31.10.2008
comment
и в таком случае, если я скажу ‹? super C ›в чем будет разница? - person Pablo Fernandez; 31.10.2008
comment
Просто хотел порекомендовать еще одну ссылку на Java Generics: angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html - person Zach Scrivena; 01.11.2008
comment
@Pablo: <? super C> будет означать, что ваш тип ограничен чем-то выше C в иерархии типов. (Извините за очень поздний ответ. Думаю, у нас не было уведомлений о комментариях 2 года назад?) - person Bill the Lizard; 05.11.2010

Если у вас есть иерархия классов 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.

person oxbow_lakes    schedule 30.10.2008
comment
Я предполагаю, что List<? super B> описывает как List принимает тип, который является родительским классом класса B? Вот почему C и D не компилируются? - person Volkan Güven; 24.01.2019

У Джоша Блоха также есть хорошее объяснение того, когда использовать super и extends в этом видео google io. talk, где он упоминает мнемонику Producer extends Consumer super.

Из слайдов презентации:

Предположим, вы хотите добавить массовые методы в Stack<E>

void pushAll(Collection<? extends E> src);

- src - производитель E

void popAll(Collection<? super E> dst);

- dst - потребитель E

person blank    schedule 31.10.2008
comment
Я читал книгу Блоха, но до сих пор не вижу разницы между extends и super в данном конкретном случае. - person Pablo Fernandez; 31.10.2008
comment
Посмотрите видео, я думаю, это довольно понятно. Кроме того, я думаю, вам следует задать еще один вопрос, в чем разница между List ‹? расширяет T ›и List‹? super T ›, где вы, надеюсь, получите больше ответов. (если да, добавьте ссылку отсюда) - person blank; 31.10.2008
comment
Кровати - почему бы не включить пример в этот ответ, чтобы сделать его законченным и автономным. Ссылки эфемерны. - person James Schek; 01.11.2008

Могут быть случаи, когда вы захотите ограничить типы типов, которые разрешено передавать параметру типа. Например, метод, работающий с числами, может захотеть принимать только экземпляры 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).

person Sandeep Kumar    schedule 08.01.2015

В целом,

Если структура содержит элементы с типом формы ? 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));
         }
    }
person Prateek Joshi    schedule 20.06.2015

Общие подстановочные знаки созданы, чтобы сделать методы, работающие с Collection, более пригодными для повторного использования.

Например, если у метода есть параметр List<A>, мы можем передать этому методу только List<A>. При некоторых обстоятельствах это бесполезная трата времени для работы этого метода :

  1. Если этот метод только читает объекты из List<A>, тогда нам должно быть разрешено передать List<A-sub> этому методу. (Потому что A-sub ЯВЛЯЕТСЯ A)
  2. Если этот метод только вставляет объекты в List<A>, тогда нам должно быть разрешено передать List<A-super> этому методу. (Потому что A - это супер)
person taoxiaopang    schedule 07.12.2016