Подтвердить что ты не робот

Разница между параметром "Ограниченный тип" и "Верхняя граница"

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

Предположим, что у вас есть два метода:

// 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.

Но какая разница между этими двумя методами в конце концов?

4b9b3361

Ответ 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);
}