Java Generics (Подстановочные Знаки)


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

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

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

7 97

7 ответов:

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

учебники Java имеют некоторые довольно хорошие объяснения дженериков в статьях Шаблоны и больше удовольствия с Подстановочными символами.

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

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

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

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

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

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

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

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

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

Collection<? extends MyObject> 

означает, что он может принимать все объекты, которые ЭТО - отношения с 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));
         }
    }

Требования До

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 (B и C). Тогда вы можете рискнуть, что listA содержит не-B объекты. Когда вы тогда попытаетесь взять предметов listB вы можете рисковать, чтобы получить не-b объекты (например, A или C). Это нарушает контракт listB переменной.

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

цель

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

вы должны использовать List <? extends A>(верхний предел) если вы собираетесь читать .получить() из списка

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

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

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

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

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

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

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

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

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