Java Generics (Подстановочные Знаки)
у меня есть несколько вопросов об общих подстановочных знаках в Java:
в чем разница между
List<? extends T>
иList<? super T>
?Что такое ограниченный подстановочный знак и что такое неограниченный подстановочный знак?
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>
к этому методу. Это отходы для функции этого метода при некоторых обстоятельствах:
- если этот метод только считывает объекты из
List<A>
, тогда мы должны датьList<A-sub>
к этому методу. (Потому что a-sub - это A)- если этот метод только вставляет объекты
List<A>
, тогда мы должны датьList<A-super>
к этому методу. (Потому что а-это а-супер)