Java Generics (подстановочные знаки) - PullRequest
103 голосов
/ 31 октября 2008

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

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

  2. Что такое ограниченный символ и что такое неограниченный символ?

Ответы [ 7 ]

117 голосов
/ 31 октября 2008

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

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

49 голосов
/ 31 октября 2008

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

39 голосов
/ 31 октября 2008

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

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

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

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

- src является производителем E

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

- DST является потребителем E

3 голосов
/ 08 января 2015

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

2 голосов
/ 20 июня 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));
         }
    }
1 голос
/ 03 марта 2018

Предварительные требования

public class A { }
public class B extends A { }
public class C extends A { }

List<A> listA = new ArrayList<A>();
List<B> listB = new ArrayList<B>();

Проблема

listB = listA; //not compiled
listA = listB; //not compiled

listB = listA; В списке A вы можете вставить объекты, которые являются экземплярами A или подклассами A (B и C). Тогда вы могли бы рискнуть, что listA содержит не-B объекты. Затем, когда вы попытаетесь извлечь объекты из listB, вы рискуете получить объекты, не относящиеся к B (например, A или C). Это нарушает договор объявления переменной listB.

listA = listB; Если бы вы могли сделать это назначение, можно было бы вставить экземпляров A и C в список, на который указывает listB. Вы можете сделать это с помощью ссылки listA, которая объявлена ​​как List. Таким образом, вы можете вставить объекты не-B в список, объявленный для хранения экземпляров B (или подкласса B).

Цель

При создании многократно используемых методов, которые работают с коллекциями определенный тип.

Вам следует использовать List <? extends A> ( верхняя граница ), если вы собираетесь читать .get () из списка

Когда вы знаете, что экземпляры в коллекции являются экземплярами A или подклассами A, безопасно читать экземпляры коллекции и приводить их к экземплярам A.

Вы не можете вставить элементы в список, потому что вы не знаете, относится ли список к классу A, B или C.

Вам следует использовать List <? super A> ( нижняя граница ), если вы собираетесь вставить .add () в список

Когда вы знаете, что список типизирован либо для A, либо для суперкласса A, безопасно вставить экземпляров A или подклассов A (например, B или C) в список. 1059 *

Вы не можете прочитать из списка, за исключением случаев, когда он переводит прочитанные объекты в Object. Элементы, уже присутствующие в списке, могут относиться к любому типу, который является либо A, либо суперклассом A, но невозможно точно знать, какой это класс.

Пожалуйста Подробнее читайте здесь - http://tutorials.jenkov.com/java-generics/wildcards.html

0 голосов
/ 07 декабря 2016

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

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

  1. Если этот метод читает только объекты из List<A>, то нам следует разрешить присвоить List<A-sub> этому методу. (Потому что A-sub - это A)
  2. Если этот метод только вставляет объекты в List<A>, то нам следует разрешить присвоить List<A-super> этому методу. (Потому что А это А-супер)
...