Разница между
этот вопрос уже есть ответ здесь:
в чем разница между List<? super T>
и List<? extends T>
?
я использовал List<? extends T>
, но это не позволяет мне добавить к нему элементы list.add(e)
, а List<? super T>
делает.
15 ответов
extends
шаблон декларации List<? extends Number> foo3
означает, что любое из этих юридических заданий:
List<? extends Number> foo3 = new ArrayList<Number>(); // Number "extends" Number (in this context)
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>(); // Double extends Number
-
чтение - учитывая вышеизложенные возможные назначения, какой тип объекта вы гарантированно читаете из
List foo3
:- вы можете прочитать
Number
потому что любой из списков, которые могут быть отнесены кfoo3
содержатNumber
или подклассNumber
. - вы не могу прочитать
Integer
, потому чтоfoo3
может указывать наList<Double>
. - вы не можете читать
Double
, потому чтоfoo3
может указывать наList<Integer>
.
- вы можете прочитать
-
писать - учитывая вышеизложенные возможные назначения, какой тип объекта вы могли бы добавить в
List foo3
Это было бы законно для все возможные вышеArrayList
задачи:- вы не можете добавить
Integer
, потому чтоfoo3
может указывайте наList<Double>
. - вы не можете добавить
Double
, потому чтоfoo3
может указывать наList<Integer>
. - вы не можете добавить
Number
, потому чтоfoo3
может указывать наList<Integer>
.
- вы не можете добавить
вы не можете добавить какой-либо объект в List<? extends T>
потому что вы не можете гарантировать какой List
это действительно указывает, поэтому вы не можете гарантировать, что объект разрешен в этом List
. Единственная "гарантия" заключается в том, что вы можете только читать из него, и вы получите T
или подкласс T
.
super
Теперь рассмотрим List <? super T>
.
шаблон декларации List<? super Integer> foo3
означает, что любое из этих юридических заданий:
List<? super Integer> foo3 = new ArrayList<Integer>(); // Integer is a "superclass" of Integer (in this context)
List<? super Integer> foo3 = new ArrayList<Number>(); // Number is a superclass of Integer
List<? super Integer> foo3 = new ArrayList<Object>(); // Object is a superclass of Integer
-
чтение - учитывая вышеизложенные возможные назначения, какой тип объекта вы гарантированно получите при чтении из
List foo3
:- вы не гарантированный
Integer
, потому чтоfoo3
может указывать наList<Number>
илиList<Object>
. - вам не гарантируется
Number
, потому чтоfoo3
может указывать наList<Object>
. - на только гарантия заключается в том, что вы получите экземпляр
Object
или подклассObject
(но вы не знаете, что подкласс).
- вы не гарантированный
-
писать - учитывая вышеизложенные возможные назначения, какой тип объекта можно добавить в
List foo3
Это было бы законно для все возможные вышеArrayList
задачи:- вы можете добавить
Integer
, потому чтоInteger
допускается в любом из вышеперечисленных списков. - вы можете добавить экземпляр подкласса
Integer
потому что экземпляр подклассаInteger
допускается в любом из вышеперечисленных списков. - вы не можете добавить
Double
, потому чтоfoo3
может указывать наArrayList<Integer>
. - вы не можете добавить
Number
, потому чтоfoo3
может указывать наArrayList<Integer>
. - вы не можете добавить
Object
, потому чтоfoo3
может указывать наArrayList<Integer>
.
- вы можете добавить
Печ
помните Печ: "Производитель Расширяется, Потребитель Супер".
"Producer Extends" - Если вам нужен
List
для производстваT
значения (вы хотите прочитатьT
s из списка), вам нужно объявить его с? extends T
, например,List<? extends Integer>
. Но вы не можете добавить к этому списку."Потребительского Супер" - Если вам нужен
List
для потребленияT
значения (вы хотите написатьT
s в список), вам нужно объявить его с? super T
, например,List<? super Integer>
. Но нет никаких гарантий, какой тип объекта вы можете прочитать из этого списка.если нужно и читать и напишите в список, вам нужно объявить его точно без подстановочных знаков, например
List<Integer>
.
пример
Примечание этот пример из Java Generics FAQ. Обратите внимание, как исходный список src
(список производителей) использует extends
, и пунктом списка dest
(список потребления) использует super
:
public class Collections {
public static <T> void copy(List<? super T> dest, List<? extends T> src) {
for (int i = 0; i < src.size(); i++)
dest.set(i, src.get(i));
}
}
Также см. как добавить в список расширяет число> структуры данных?
представьте себе эту иерархию
1. Удлиняет
писать
List<? extends C2> list;
ты говоришь, что list
будет ссылаться на объект типа (например) ArrayList
чей общий тип является одним из 7 подтипов of C2
(C2
входит в комплект):
-
C2:
new ArrayList<C2>();
, (объект, который может хранить C2 или подтипы) или -
D1:
new ArrayList<D1>();
, (объект, который может хранить D1 или подтипы) или -
D2:
new ArrayList<D2>();
, (объект, который может хранить D2 или подтипы) или...
и так далее. Семь разных случаев:
1) new ArrayList<C2>(): can store C2 D1 D2 E1 E2 E3 E4
2) new ArrayList<D1>(): can store D1 E1 E2
3) new ArrayList<D2>(): can store D2 E3 E4
4) new ArrayList<E1>(): can store E1
5) new ArrayList<E2>(): can store E2
6) new ArrayList<E3>(): can store E3
7) new ArrayList<E4>(): can store E4
у нас есть набор типов "storable" для каждого возможного случая: 7 (красных) наборов здесь графически представлены
как вы можете видеть, нет безопасный тип это общее для каждого случая:
- нельзя
list.add(new C2(){});
потому что это может бытьlist = new ArrayList<D1>();
- нельзя
list.add(new D1(){});
потому что это может бытьlist = new ArrayList<D2>();
и так далее.
2. Супер!--43-->
писать
List<? super C2> list;
ты говоришь, что list
будет ссылаться на объект типа (например) ArrayList
чей общий тип является одним из 7 супертипы из C2
(C2
входит в комплект):
-
А1:
new ArrayList<A1>();
, (объект, который может хранить A1 или подтипы) или -
А2:
new ArrayList<A2>();
, (объект, который может хранить A2 или подтипы) или -
A3:
new ArrayList<A3>();
, (объект, который может хранить A3 или подтипы) или...
и так далее. Семь разных случаев:
1) new ArrayList<A1>(): can store A1 B1 B2 C1 C2 D1 D2 E1 E2 E3 E4
2) new ArrayList<A2>(): can store A2 B2 C1 C2 D1 D2 E1 E2 E3 E4
3) new ArrayList<A3>(): can store A3 B3 C2 C3 D1 D2 E1 E2 E3 E4
4) new ArrayList<A4>(): can store A4 B3 B4 C2 C3 D1 D2 E1 E2 E3 E4
5) new ArrayList<B2>(): can store B2 C1 C2 D1 D2 E1 E2 E3 E4
6) new ArrayList<B3>(): can store B3 C2 C3 D1 D2 E1 E2 E3 E4
7) new ArrayList<C2>(): can store C2 D1 D2 E1 E2 E3 E4
у нас есть набор типов "storable" для каждого возможного случая: 7 (красный) наборы здесь графически представлены
как вы можете видеть, здесь у нас семь безопасных видов, которые являются общими для каждого случая: C2
, D1
, D2
, E1
, E2
, E3
, E4
.
- вы можете
list.add(new C2(){});
потому что, независимо от вида списка, на который мы ссылаемся,C2
разрешено - вы можете
list.add(new D1(){});
потому что, независимо от вида списка, на который мы ссылаемся,D1
разрешено
и так далее. Вероятно, вы заметили, что эти типы соответствуют иерархии, начиная с типа C2
.
Примечания
здесь полная иерархия, если вы хотите сделать некоторые тесты
interface A1{}
interface A2{}
interface A3{}
interface A4{}
interface B1 extends A1{}
interface B2 extends A1,A2{}
interface B3 extends A3,A4{}
interface B4 extends A4{}
interface C1 extends B2{}
interface C2 extends B2,B3{}
interface C3 extends B3{}
interface D1 extends C1,C2{}
interface D2 extends C2{}
interface E1 extends D1{}
interface E2 extends D1{}
interface E3 extends D2{}
interface E4 extends D2{}
мне нравится ответ от @Bert F, но мой мозг видит это так.
У меня в руке крестик. Если я захочу написать мой X в список, этот список должен быть либо списком X, либо списком вещей, на которые мой X может быть поднят, когда я пишу их, т. е. любой суперкласса Х...
List<? super X>
Если я получу список, и я хочу читать X из этого списка, что лучше быть список X или список вещей, которые могут быть переданы X как я их зачитал, то есть все, что выходит X
List<? extends X>
надеюсь, что это помогает.
на основе ответ Берта Ф я хотел бы объяснить мое понимание.
допустим у нас есть 3 класса как
public class Fruit{}
public class Melon extends Fruit{}
public class WaterMelon extends Melon{}
здесь мы имеем
List<? extends Fruit> fruitExtendedList = …
//Says that I can be a list of any object as long as this object extends Fruit.
Ok теперь давайте попробуем получить некоторое значение из fruitExtendedList
Fruit fruit = fruitExtendedList.get(position)
//This is valid as it can only return Fruit or its subclass.
еще раз попробуем
Melon melon = fruitExtendedList.get(position)
//This is not valid because fruitExtendedList can be a list of Fruit only, it may not be
//list of Melon or WaterMelon and in java we cannot assign sub class object to
//super class object reference without explicitly casting it.
то же самое относится к
WaterMelon waterMelon = fruitExtendedList.get(position)
теперь давайте попробуем установить какой-то объект в fruitExtendedList
добавлять фрукты объект
fruitExtendedList.add(new Fruit())
//This in not valid because as we know fruitExtendedList can be a list of any
//object as long as this object extends Fruit. So what if it was the list of
//WaterMelon or Melon you cannot add Fruit to the list of WaterMelon or Melon.
добавление объекта дыни
fruitExtendedList.add(new Melon())
//This would be valid if fruitExtendedList was the list of Fruit but it may
//not be, as it can also be the list of WaterMelon object. So, we see an invalid
//condition already.
наконец, давайте попробуем добавить объект арбуз
fruitExtendedList.add(new WaterMelon())
//Ok, we got it now we can finally write to fruitExtendedList as WaterMelon
//can be added to the list of Fruit or Melon as any superclass reference can point
//to its subclass object.
но ждать что, если кто-то решит сделать новый тип лимона, скажем, для Аргументов sake SaltyLemon as
public class SaltyLemon extends Lemon{}
теперь fruitExtendedList может быть список фруктов, дыни, арбуза или SaltyLemon.
Итак, наше заявление
fruitExtendedList.add(new WaterMelon())
не действительный либо.
в основном мы можем сказать, что мы ничего не можем написать fruitExtendedList.
это подводит итог List<? extends Fruit>
теперь давайте посмотрим
List<? super Melon> melonSuperList= …
//Says that I can be a list of anything as long as its object has super class of Melon.
теперь давайте попробуем получить некоторое значение от melonSuperList
Fruit fruit = melonSuperList.get(position)
//This is not valid as melonSuperList can be a list of Object as in java all
//the object extends from Object class. So, Object can be super class of Melon and
//melonSuperList can be a list of Object type
аналогично дыня, арбуз или любой другой объект не может быть прочитан.
но обратите внимание, что мы можем читать тип объекта примеры
Object myObject = melonSuperList.get(position)
//This is valid because Object cannot have any super class and above statement
//can return only Fruit, Melon, WaterMelon or Object they all can be referenced by
//Object type reference.
теперь давайте попробуем установить некоторое значение из melonSuperList.
добавление объекта типа object
melonSuperList.add(new Object())
//This is not valid as melonSuperList can be a list of Fruit or Melon.
//Note that Melon itself can be considered as super class of Melon.
добавление объекта типа фруктов
melonSuperList.add(new Fruit())
//This is also not valid as melonSuperList can be list of Melon
добавление объекта типа дыни
melonSuperList.add(new Melon())
//This is valid because melonSuperList can be list of Object, Fruit or Melon and in
//this entire list we can add Melon type object.
добавление объекта типа арбуза
melonSuperList.add(new WaterMelon())
//This is also valid because of same reason as adding Melon
чтобы подвести итог, мы можем добавить дыню или ее подкласс в melonSuperList и прочитать только объект типа object.
super-нижняя граница, а extends-верхняя граница.
по данным http://download.oracle.com/javase/tutorial/extra/generics/morefun.html :
решение заключается в использовании форма ограниченный подстановочный знак, который мы еще не видели: маски с нижней границей. Этот синтаксис ? super T обозначает неизвестное тип, который является супертипом T (или T сам; помните, что супертип отношение рефлексивно). Это двойной ограниченных маски мы использование, где мы используем ? удлиняет Т обозначить неизвестный тип, который является подтип т.
используя выходит вы можете получить только из коллекции. Вы не можете вложить в него. Кроме того, хотя супер позволяет как получить, так и положить, тип возврата во время get ? super T.
самое запутанное здесь то, что какие бы ограничения типа мы не указывали, назначение работает только одним способом:
baseClassInstance = derivedClassInstance;
вы можете подумать, что Integer extends Number
и Integer
будет <? extends Number>
, но компилятор скажет вам, что <? extends Number> cannot be converted to Integer
(то есть, на человеческом языке,это неправильно, что все, что расширяет число может быть преобразовано в целое число):
class Holder<T> {
T v;
T get() { return v; }
void set(T n) { v=n; }
}
class A {
public static void main(String[]args) {
Holder<? extends Number> he = new Holder();
Holder<? super Number> hs = new Holder();
Integer i;
Number n;
Object o;
// Producer Super: always gives an error except
// when consumer expects just Object
i = hs.get(); // <? super Number> cannot be converted to Integer
n = hs.get(); // <? super Number> cannot be converted to Number
// <? super Number> cannot be converted to ... (but
// there is no class between Number and Object)
o = hs.get();
// Consumer Super
hs.set(i);
hs.set(n);
hs.set(o); // Object cannot be converted to <? super Number>
// Producer Extends
i = he.get(); // <? extends Number> cannot be converted to Integer
n = he.get();
o = he.get();
// Consumer Extends: always gives an error
he.set(i); // Integer cannot be converted to <? extends Number>
he.set(n); // Number cannot be converted to <? extends Number>
he.set(o); // Object cannot be converted to <? extends Number>
}
}
hs.set(i);
это нормально, потому что Integer
может быть преобразован в любой суперкласс Number
(и не потому что Integer
- это суперкласс Number
, что неверно).
EDIT добавлен комментарий о Consumer Extends и Producer Super -- они не имеют смысла, потому что они указывают, соответственно, ничего и просто Object
. Вам советуют помнить о грудных мышцах, потому что белые грибы никогда не полезны.
общие подстановочные знаки предназначены для двух основных потребностей:
чтение из общей коллекции Вставка в общую коллекцию Существует три способа определения коллекции (переменной) с помощью общих подстановочных знаков. Это:
List<?> listUknown = new ArrayList<A>();
List<? extends A> listUknown = new ArrayList<A>();
List<? super A> listUknown = new ArrayList<A>();
List<?>
означает список, типизированный для неизвестного типа. Это может быть List<A>
, a List<B>
, a List<String>
etc.
List<? extends A>
значит, список объектов, которые являются экземплярами class A
или subclasses of A
(например, B и C).
List<? super A>
это означает, что список набирается либо A class
или superclass of A
.
Подробнее:http://tutorials.jenkov.com/java-generics/wildcards.html
когда использовать extends и super
маски наиболее полезны в параметрах метода. Они учитывают необходимую гибкость в интерфейсах метода.
люди часто путают, когда использовать extends и когда использовать супер границы. Эмпирическое правило-это принцип get-put. Если вы получаете что-то из параметризованного контейнера, используйте extends.
int totalFuel(List<? extends Vehicle> list) {
int total = 0;
for(Vehicle v : list) {
total += v.getFuel();
}
return total;}
метод totalFuel получает транспортные средства из списка, спрашивает их о том, как у них много топлива, и они подсчитывают общее количество. Если вы помещаете объекты в параметризованный контейнер, используйте super.
int totalValue(Valuer<? super Vehicle> valuer) {
int total = 0;
for(Vehicle v : vehicles) {
total += valuer.evaluate(v);
}
return total;}
метод totalValue помещает транспортные средства в оценщик. Полезно знать, что extends bound гораздо более распространен, чем super.
вы можете пройти через все ответы выше, чтобы понять, почему .add()
ограничен '<?>'
, '<? extends>'
и частично '<? super>'
.
но вот вывод из всего этого, если вы хотите запомнить его, и не хотите каждый раз исследовать ответ:
List<? extends A>
означает, что это примет любой List
of A
и подкласс A
.
Но вы ничего не можете добавить к этому списку. Даже объектов типа A
.
List<? super A>
значит это примет любой список A
и суперкласс A
.
Вы можете добавлять объекты типа A
и его подклассов.
up проголосовали ответы охватывает детали по многим аспектам. Однако я постараюсь ответить на этот вопрос по-другому.
есть 2 вещи, которые мы должны рассмотреть,
1. Присвоение переменной списка
List<? extends X> listvar;
здесь любая список X или список подклассов X может быть назначен в listvar.
List<? extends Number> listvar;
listvar = new ArrayList<Number>();
listvar = new ArrayList<Integer>();
List<? super X> listvar;
здесь любая список X или список суперклассов X может быть назначено в listvar.
List<? super Number> listvar;
listvar = new ArrayList<Number>();
listvar = new ArrayList<Object>();
2. Выполните операцию чтения или записи в переменной списка
`List<? extends X> listvar;`
вы можете использовать эту функцию, чтобы принять список в аргументах метода и выполнить любые операции над тип X (Примечание: Вы можете читать только объекты типа X из списка).
`List<? super Number> listvar;
вы можете использовать эту функцию, чтобы принять список в аргументах метода и выполнить любые операции на тип как вы можете читать только объекты типа Object из списка. Но да, дополнительная вещь здесь, вы можете добавить объекты типа X в список.
список не позволяет добавлять в список ничего, кроме null.
список позволяет добавлять все, что есть-X (X или его подкласс) или null.
Я хотел бы показать разницу. Предположим, мы имеем:
class A { }
class B extends A { }
class C extends B { }
List<? extends T>
- значение и назначение:
|-------------------------|-------------------|---------------------------------|
| wildcard | get | assign |
|-------------------------|-------------------|---------------------------------|
| List<? extends C> | A B C | List<C> |
|-------------------------|-------------------|---------------------------------|
| List<? extends B> | A B | List<B> List<C> |
|-------------------------|-------------------|---------------------------------|
| List<? extends A> | A | List<A> List<B> List<C> |
|-------------------------|-------------------|---------------------------------|
List<? super T>
- написание и назначение:
|-------------------------|-------------------|--------------------------------------------|
| wildcard | add | assign |
|-------------------------|-------------------|--------------------------------------------|
| List<? super C> | C | List<Object> List<A> List<B> List<C> |
|-------------------------|-------------------|--------------------------------------------|
| List<? super B> | B C | List<Object> List<A> List<B> |
|-------------------------|-------------------|--------------------------------------------|
| List<? super A> | A B C | List<Object> List<A> |
|-------------------------|-------------------|--------------------------------------------|
во всех случаях:
- вы всегда можете get
Object
из списка независимо от подстановочного знака.
- вы всегда можете добавить
null
в изменяемый список независимо от подстановочного знака.
|
v
список ? может быть любой подкласс of
X
и самX
.список ? может быть любой суперкласса of
X
и самX
.
^
|
пример, Порядок наследования принимается как O > S > T > U > V
используя ключевое слово extends ,
правильно:
List<? extends T> Object = new List<T>();
List<? extends T> Object = new List<U>();
List<? extends T> Object = new List<V>();
неправильно:
List<? extends T> Object = new List<S>();
List<? extends T> Object = new List<O>();
супер сайта:
правильно:
List<? super T> Object = new List<T>();
List<? super T> Object = new List<S>();
List<? super T> Object = new List<O>();
неправильно:
List<? super T> Object = new List<U>();
List<? super T> Object = new List<V>();
Добавить объект: List Object = новый список ();
Object.add(new T()); //error
но почему ошибка ? Давайте рассмотрим возможности инициализации объекта List
List<? extends T> Object = new List<T>();
List<? extends T> Object = new List<U>();
List<? extends T> Object = new List<V>();
если мы используем объект.добавить (new T ()); тогда это будет правильно, только если
List<? extends T> Object = new List<T>();
но есть дополнительные две возможности
список Объект = новый список(); List Object = новый список(); Если мы попытаемся добавить (новый T ()) к вышеупомянутым двум возможностям, это даст ошибку, потому что T-высший класс U и V . мы пытаемся добавить объект T [который является (new T ())] в список типов U и V . Объект более высокого класса(базовый класс) не может быть передан объекту более низкого класса (подклассу).
из-за дополнительных двух возможностей Java дает вам ошибку, даже если вы используете правильное равновесие, поскольку Java не знает, какой объект вы имеете в виду - ... Таким образом, вы не можете добавлять объекты в List Object = new List(); поскольку есть возможности, которые недопустимы.
Добавить объект: List Object = новый список ();
Object.add(new T()); // compiles fine without error
Object.add(new U()); // compiles fine without error
Object.add(new V()); // compiles fine without error
Object.add(new S()); // error
Object.add(new O()); // error
но почему ошибка возникает в вышеупомянутых двух ? мы можем использовать объект.добавить (new T ()); только по нижеприведенным возможностям,
List<? super T> Object = new List<T>();
List<? super T> Object = new List<S>();
List<? super T> Object = new List<O>();
если мы попытаемся использовать Object.добавить(новый T()) в List Object = новый список(); и List Object = новый список(); тогда это даст ошибку Это потому что Мы не можем добавить T объект[который является новым T ()] для объекта List = new List (); потому что это объект типа U . Мы не можем добавить объект T[который является новым T()] к объекту U, потому что T-базовый класс, а U-подкласс . Мы не можем добавить базовый класс подкласс и поэтому возникает ошибка . То же самое в другом случае .