Понимание верхней и нижней границ? в Java Generics
мне действительно трудно понять параметр wild card. У меня есть несколько вопросов по этому поводу.
?
в качестве параметра типа можно использовать только в методах. например:printAll(MyList<? extends Serializable>)
Я не могу определить классы с?
параметр, как тип.я понимаю верхнюю границу
?
.printAll(MyList<? extends Serializable>)
означает: "printAll
печатиMyList
если у него есть объекты, реализующиеSerialzable
взаимодействие."
У меня есть небольшая проблема сsuper
.printAll(MyList<? super MyClass>)
означает: "printAll
печатиMyList
если у него есть объектыMyClass
или любой класс, который расширяетMyClass
(потомкиMyClass
)."
поправьте меня, где я ошибся.
короче, только T
или E
или K
или V
или N
может использоваться в качестве параметров типа для определения универсальных классов. ?
только использовать в методах
обновление 1:
public void printAll(MyList<? super MyClass>){
// code code code
}
согласно Книге Айвора Хортона,MyList<? super MyClass>
означает, что я могу напечатать MyList
если у него есть объекты MyClass
или любой из интерфейсов или классов, которые он реализует. То есть, MyClass
это нижняя граница. Это последний класс в иерархии наследования. Это значит, что мое первоначальное предположение было неверным.
так, скажем, если MyClass
похоже,:
public class MyClass extends Thread implements ActionListener{
// whatever
}
затем, printAll()
будет печатать, если
1. Есть объекты MyClass
в списке
2. Есть объекты Thread
или ActionListener
на List
обновление 2:
Итак, прочитав множество ответов на вопрос, вот мое понимание:
? extends T
означает любой класс, который расширяетT
. Таким образом, мы имеем в виду детиT
. Следовательно,T
- верхняя граница. Самый верхний класс в иерархии наследования? super T
означает любой класс / интерфейс, которыйsuper
ofT
. Таким образом, мы имеем в виду все родителямT
.T
таким образом, нижняя граница. Самый низкий класс в иерархии наследования
6 ответов
?
в качестве параметра типа можно использовать только в методах. например:printAll(MyList<? extends Serializable>)
Я не могу определить классы с?
параметр, как тип.
шаблон (?
) не является формальным параметром типа, А может использоваться как аргумент типа. В приведенном примере,? extends Serializable
приведен в качестве аргумента типа для универсального типа MyList
о printAll
параметр метода.
методы можно также объявить тип параметры как классы, например:
static <T extends Serializable> void printAll(MyList<T> myList)
я понимаю верхнюю границу
?
.printAll(MyList<? extends Serializable>)
означает printAll будет печатать MyList, если у него есть объекты, реализующие интерфейс Serialzable
точнее, это означает вызов printAll
будет компилироваться, только если он принят MyList
С некоторым общим типом, который является или реализует Serializable
. В этом случае он примет MyList<Serializable>
, MyList<Integer>
, так далее.
у меня есть небольшая проблема с
super
.printAll(MyList<? super MyClass>)
означает printAll будет печатать MyList, если у него есть объекты MyClass или любой класс, который расширяет MyClass (потомки MyClass)
подстановочный знак, ограниченный super
это ниже связаны. Чтобы мы могли сказать вызов printAll
будет компилироваться, только если он принят MyList
С некоторым общим типом, который MyClass
или какой-то супер-типа MyClass
. Так что в этом случае он примет MyList<MyClass>
, например,MyList<MyParentClass>
или MyList<Object>
.
Итак, скажите, если MyClass выглядит так:
public class MyClass extends Thread implements ActionListener{ // whatever }
затем printAll () напечатает if
- в списке есть объекты MyClass
- в списке есть объекты потока или ActionListener
вы на правильном пути. Но я думаю, что, например, " он будет печатать, если есть объекты MyClass
в списке" проблематично. Это звучит так, как будто вы определяете поведение среды выполнения - дженерики все о проверках времени компиляции. Например, не смог бы передать MyList<MySubclass>
в качестве аргумента MyList<? super MyClass>
, хотя он может содержать экземпляры MyClass
, в порядке наследования. Я бы перефразировал его так:--54-->
вызов printAll(MyList<? super MyClass>)
будет компилироваться, только если она передается a:
MyList<MyClass>
MyList<Thread>
MyList<Runnable>
MyList<ActionListener>
MyList<EventListener>
MyList<Object>
-
MyList<? super X>
здесьX
isMyClass
,Thread
,Runnable
,ActionListener
,EventListener
илиObject
.
Итак, после прочтения многих ответов на вопрос, вот мой понимание:
? extends T
означает любой класс, который расширяет T. Таким образом, мы имеем в виду следовательно, дети т.--122-->T-верхняя граница. Высший класс в иерархии наследования
? super T
означает любой класс / интерфейс, которыйsuper
т. Таким образом, мы обращаясь ко всем родителям т. T, таким образом, нижняя граница. Этот самый низкий класс в иерархии наследования
близко, но я бы не сказал "Дети T
" или "родители T
", так как эти границы включительно - точнее было бы сказать"T
или его подтипов" и "T
или его супертипов".
в первую очередь T
или E
или K
или что-то не фиксированные имена. Это просто переменные типа, и вы сами решаете, как их назвать. T
, E
, K
- это просто примеры, но вы можете назвать это Foo
или что-то еще.
сейчас на ваш первый вопрос: после подстановки ?
представляет тип "any and unknown", неуказанный, нет смысла объявлять класс generic над неуказанным типом. Полезно иметь подстановочный знак в параметров методов или переменных, когда вы не заботитесь о типе.
Теперь относительно вашего второго вопроса: нижняя граница дает еще большую гибкость вашим общим методам. оба!--8--> и super
, напротив
-
? extends T
: неизвестный тип, который является подтипомT
-
? super T
: неизвестный тип, который является супер типаT
последнее может быть полезно, когда вы хотите принять тип это совместимо с T (так что T-это тот тип). Практический пример можно найти здесь.
начнем с самого начала.
строго говоря любой допустимый идентификатор java может использоваться как параметр универсального типа - это просто специальный тип переменной:
public static final class MyGenericClass<MyGenericType> {
}
является совершенно действительным Java.
Далее, вы можете использовать ?
везде, где вы можете сделать заявление. Вы можете использовать подстановочный знак при объявлении переменных, но не при создать них:
public static final class MyGenericClass {
private final Collection<? extends String> myThings;
public MyGenericClass(Collection<? extends String> myThings) {
this.myThings = myThings;
}
public void doStuff(final Collection<? extends String> myThings) {
}
}
снова все действительно, Вы не может этого:
final Collection<? extends String> myThings = new ArrayList<? extends String>();
когда дело доходит до extends
vs super
это называется co-дисперсия против contra-дисперсии. Он определяет, в каком направлении по иерархии классов типов могут путешествовать:
final Collection<? extends Runnable> example1 = new ArrayList<Runnable>();
final Collection<? extends Runnable> example2 = new ArrayList<TimerTask>();
final Collection<? super Runnable> example3 = new ArrayList<Runnable>();
final Collection<? super Runnable> example4 = new ArrayList<Object>();
первые два примера демонстрируют extends
- плотно связаны, можно предположить, с Collection
и Runnable
как пользователь может пройти Collection
что есть Runnable
в его иерархии наследования.
вторые два примера демонстрируют super
- плотно связаны, можно предположить, с Collection
и Object
как мы разрешаем все, что есть на иерархия наследования Runnable
.
Хммм ваше заявление на super
( printAll(MyList<? super MyClass>)
) не понятно. Что это значит, если предположить, что Myclass расширяется Object
вы можете printAll(MyList<MyClass>)
и printAll(MyList<Object>)
но ничего больше.... это означает, что общий тип MyList должен быть суперклассом (а не подклассом) MyClass. Это отличается от того, что ты сказал.
для первого вопроса: Вы не можете определить метод с ?
в качестве параметра тип. Следующее не будет компилироваться:
void <?> foo() {}
?
используется для привязки к другим дженерикам без указания параметра type. Вы можете написать для методов:
void foo(List<?> e) {}
и вы также можете писать на классы:
public class Bar<E extends List<?>> { }
использовать super
:
public void printAll(MyList<? super MyClass>){
// code code code
}
это не будет, как вы говорите, печатать список " если у него есть объекты Класса MyClass". Он может иметь объекты любого класса, который является подклассом класса, который является родителем MyClass. Компилятор не знает во время компиляции, какие объекты будут в списке в любом случае.
чтобы обойти это, рассмотрим простой пример с Number
иерархия классов. Float
и Integer
дети Number
. Вы можете написать свой метод так:
public void printAll(List<? super Float>){
// code code code
}
тогда вы можете вызвать этот метод с помощью List<Number>
:
List<Number> numbers = new ArrayList<>();
numbers.add(1); // actually only add an Integer
printAll(numbers); // compiles.
это возможно, было бы не очень полезно в этом случае. Где было бы полезно, например, когда вы хотите добавить Float
в коллекцию, не желая, чтобы это был только список, например:
public void addFloat(List<? super Float> list){
list.add(2.5);
}
?
также может использоваться как метод return type
List<? extends Number> xxx() {
...
}
или как поле или переменную типа
List<? extends Number> xxx = ...
List<? super Nember> xxx = ...