2016-01-25 4 views
8

Я пытаюсь научиться Java Generics подстановочные, прочитав следующее: http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ103Различия между `копия (список <? super T> Dest, Список <? extends T> ЦСИ)` и `копирования (Список <T> Dest, Список <? extends T> ЦСИ)`

Там является одним из примеров в материале:

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)); 
    } 
} 

было интересно, если я могу изменить подпись метода в следующем виде:

public static <T> void copy(List<? super T> dest, List<? extends T> src) { 

public static <T> void copy(List<T> dest, List<? extends T> src) { 

Существуют ли какие-либо различия между этими двумя метод sinatures?

Примеры были бы оценены.

+2

Найдено большое объяснение здесь: http://stackoverflow.com/questions/4343202/difference-between-super-t-and-extends-t-in-java/4343547 # 4343547 –

+0

@DzmitryPaulenka Спасибо за вашу помощь, но я все еще не уверен в различиях между 'List ' и 'List ' в этом случае.Я думаю, что 'List ' работает так же, как 'List '. – Xin

ответ

5

Вы правы. В этом случае аргументы типа 2 параметра используются для выражения отношения, которое dest должно содержать объекты супертипа объектов в src. Поэтому, если вы говорите, src содержит <? extends T>, то это достаточно сказать, что dest содержит объекты Т.

Вы также можете выразить это наоборот, а именно:

List<? super T> dest, List<T> src 

к тому же эффекту.

EDIT: Я подозреваю, что автор, чтобы укрепить точку о PECS principle

+1

Не только для «симметрии», но и для отмены принципа [PECS] (http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs) (Мне не нравится описание этого «PECS», как в его короткой форме, просто двусмысленно и, возможно, вводит в заблуждение, но с подробным описанием ключевой момент должен стать ясным) – Marco13

+0

Хорошая точка - я ее отредактирую. –

4

Как matt freake pointed out in his answer, существует не так много практической разницы между

public static <T> void copyA(List<? super T> dest, List<? extends T> src) // and 
public static <T> void copyB(List<  T> dest, List<? extends T> src) 

Сниппет ниже содержит exampleShowingThatTheyAreBasicallyEquivalent.

Причина, по которой авторы решили использовать ? super T, скорее всего, они хотели подчеркнуть PECS principle: Producer extends - Consumer super.

В этом примере первый список - потребитель объектов. Он получает только объекты из другого списка. Поэтому его тип должен быть List<? super T>.

Однако фрагмент кода ниже, также содержит exampleShowingOneSubtleDifference .Я вряд ли можно думать о том случае, когда это действительно практически отношение, а просто указать на это: Когда вы обойти вывод типа, и пин-код типа <T> в один конкретный тип, вы все равно можете передать List<? super T> в качестве первого аргумента первому методу. Во втором, тип должен точно соответствовать - но это просто то, что говорит метод подписи, так что, может быть, это очевидно ...

import java.util.List; 

public class PecsExample 
{ 
    public static void exampleShowingOneSubtleDifference() 
    { 
     List<? super Number> superNumbers = null; 
     List<Number> numbers = null; 

     PecsExample.<Number>copyA(superNumbers, numbers); // Works 
     //PecsExample.<Number>copyB(superNumbers, numbers); // Does not work 
    } 

    public static void exampleShowingThatTheyAreBasicallyEquivalent() 
    { 
     List<? super Object> superObjects = null; 
     List<? super Number> superNumbers = null; 
     List<? super Integer> superIntegers = null; 

     List<Object> objects = null; 
     List<Number> numbers = null; 
     List<Integer> integers = null; 

     List<? extends Object> extendsObjects = null; 
     List<? extends Number> extendsNumbers = null; 
     List<? extends Integer> extendsIntegers = null; 

     copyA(objects, objects); 
     copyA(objects, numbers); 
     copyA(objects, integers); 
     copyA(numbers, numbers); 
     copyA(numbers, integers); 
     copyA(integers, integers); 

     copyA(superObjects, objects); 
     copyA(superObjects, numbers); 
     copyA(superObjects, integers); 
     copyA(superNumbers, numbers); 
     copyA(superNumbers, integers); 
     copyA(superIntegers, integers); 

     copyA(objects, extendsObjects); 
     copyA(objects, extendsNumbers); 
     copyA(objects, extendsIntegers); 
     copyA(numbers, extendsNumbers); 
     copyA(numbers, extendsIntegers); 
     copyA(integers, extendsIntegers); 

     copyB(objects, objects); 
     copyB(objects, numbers); 
     copyB(objects, integers); 
     copyB(numbers, numbers); 
     copyB(numbers, integers); 
     copyB(integers, integers); 

     copyB(superObjects, objects); 
     copyB(superObjects, numbers); 
     copyB(superObjects, integers); 
     copyB(superNumbers, numbers); 
     copyB(superNumbers, integers); 
     copyB(superIntegers, integers); 

     copyB(objects, extendsObjects); 
     copyB(objects, extendsNumbers); 
     copyB(objects, extendsIntegers); 
     copyB(numbers, extendsNumbers); 
     copyB(numbers, extendsIntegers); 
     copyB(integers, extendsIntegers); 
    } 

    public static <T> void copyA(List<? super T> dest, List<? extends T> src) 
    { 
     for (int i = 0; i < src.size(); i++) 
     { 
      dest.set(i, src.get(i)); 
     } 
    } 

    public static <T> void copyB(List<T> dest, List<? extends T> src) 
    { 
     for (int i = 0; i < src.size(); i++) 
     { 
      dest.set(i, src.get(i)); 
     } 
    } 
} 
+0

Спасибо за примеры. Они очень полезны. – Xin

Смежные вопросы