Разница между параметром "Ограниченный тип" и "Верхняя граница"
Я знаю, что уже был опубликован аналогичный вопрос, хотя я думаю, что у меня несколько другое...
Предположим, что у вас есть два метода:
// Bounded type parameter
private static <T extends Number> void processList(List<T> someList) {
}
// Upper bound wildcard
private static void processList2(List<? extends Number> someList) {
// ...
}
Насколько я знаю, оба метода принимают аргументы, которые являются List
типа Number
или List
подтипа Number
.
Но какая разница между этими двумя методами в конце концов?
Ответы
Ответ 1
Во время компиляции существует несколько различий между двумя синтаксисами:
- С первым синтаксисом вы можете добавлять элементы в
someList
, а со вторым - не можете. Это обычно называется PECS и менее широко известно как PUT и GET prinicple.
- С первым синтаксисом у вас есть дескриптор параметра типа
T
, поэтому вы можете использовать его для выполнения таких задач, как определение локальных переменных в методе типа T
, листинг ссылки на тип T
, методы вызова, доступные в классе, представленном T
и т.д. Но со вторым синтаксисом у вас нет дескриптора типа, поэтому вы не можете этого сделать.
-
Первый метод можно фактически вызывать из второго метода для
захватить подстановочный знак. Это наиболее распространенный способ capture a
подстановочный знак с помощью вспомогательного метода.
private static <T extends Number> void processList(List<T> someList) {
T n = someList.get(0);
someList.add(1,n); //addition allowed.
}
private static void processList2(List<? extends Number> someList) {
Number n = someList.get(0);
//someList.add(1,n);//Compilation error. Addition not allowed.
processList(someList);//Helper method for capturing the wildcard
}
Обратите внимание, что поскольку generics - это сахара времени компиляции, эти различия на более широком уровне ограничены только компиляцией.
Ответ 2
Я могу думать о следующих различиях:
a) Изменив свой список внутри метода, рассмотрите ниже код:
private static <T extends Number>void processList(List<T> someList)
{
T t = someList.get(0);
if ( t.getClass() == Integer.class )
{
Integer myNum = new Integer(4);
someList.add((T) myNum);
}
}
// Upper bound wildcard
private static void processList2(List<? extends Number> someList)
{
Object o = someList.get(0);
if ( o instanceof Integer )
{
Integer myNum = new Integer(4);
someList.add(myNum); // Compile time error !!
}
}
С помощью шаблона вы не можете добавлять элементы в список! Компилятор сообщает вам, что он не знает, что такое myNum
. Но в первом методе вы можете добавить Integer
, сначала проверив, есть ли T
Integer
, ошибка времени компиляции.
b) Первый метод называется общим методом. Он следует за синтаксисом, который определен для общего метода.
Верхние границы, указанные в определении метода, используются для ограничения типов параметров.
Второй не обязательно называется универсальным методом, это обычный метод, который принимает общий параметр.
Подстановочный знак ?
с ключевым словом extends
используется как средство расслабления типов, которые метод может принять.
Ответ 3
Разница на стороне компилятора.
На первом вы можете использовать этот тип (чтобы что-то приложить или использовать его как привязку для вызова другого метода, например), а во втором - вы не можете его использовать.
Ответ 4
Если вы хотите использовать информацию о типе, переходите к ограничению. С помощью шаблона аргумент будет отображаться как общий объект, и вы не сможете вызывать методы, основанные на этом типе.
public static <T extends Object> ListIterator<T> createListIterator(ListIterator<T> o)
{
return new ListIteratorAdaptor<T>(o);
}