2012-06-08 4 views
33

Я новичок в Generic и мой вопрос: какая разница между двумя функциями:разница между родовым типом и подстановочным типом

функция 1:

public static <E> void funct1 (List<E> list1) { 

} 

функцией 2:

public static void funct2(List<?> list) { 

} 

Спасибо.

+1

Если String ссылается на 'java.lang.String', я думаю, что funct2 довольно избыточен в подстановочных выражениях, поскольку' java.lang.String' является окончательным классом и не может быть расширен. – nhahtdh

+1

Это странно, потому что технически вы не можете расширить String со своего последнего класса. –

+0

однако нет ограничений на использование функции 2: поскольку она позволяет подклассы String (хотя это невозможно) или сам класс String ... – ria

ответ

29

Первая подпись гласит: list1 - это список Es.

Вторая подпись гласит: список - это список экземпляров какого-либо типа, но мы не знаем тип.

разница становится очевидной, когда мы пытаемся изменить метод поэтому он принимает второй аргумент, который должен быть добавлен в список внутри метода:

import java.util.List; 

public class Experiment { 
    public static <E> void funct1(final List<E> list1, final E something) { 
     list1.add(something); 
    } 

    public static void funct2(final List<?> list, final Object something) { 
     list.add(something); // does not compile 
    } 
} 

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

На самом деле я только что нашел еще лучше продемонстрировать разницу:

public class Experiment { 
    public static <E> void funct1(final List<E> list) { 
     list.add(list.get(0)); 
    } 

    public static void funct2(final List<?> list) { 
     list.add(list.get(0)); // !!!!!!!!!!!!!! won't compile !!!!!!!!! 
    } 
} 

Можно было бы и почему мы должны <?>, когда он только ограничивает то, что мы можем сделать с ним (как @Babu_Reddy_H в комментариях) , Я вижу следующие преимущества версии подстановочной:

  • Вызывающих должен знать меньше об объекте, он переходит в К примеру, если у меня есть Карта списков:. Map<String, List<?>> я могу передать его значение вашей функции без указав тип элементов списка. Итак,

  • Если я передаю объекты, параметризованные таким образом, я активно ограничиваю то, что люди знают об этих объектах и ​​что они могут с ним делать (если они держатся подальше от небезопасного литья).

Эти два смысла имеют смысл, когда я их объединяю: List<? extends T>. Например, рассмотрим метод List<T> merge(List<? extends T>, List<? extends T>), который объединяет два входных списка в новый список результатов. Конечно, вы могли бы ввести еще два типа параметров, но зачем вам это нужно? Это было бы над определением вещей.

  • наконец подстановочные может иметь более низкие оценки, поэтому со списками вы можете сделать add метод работы, в то время как get не дает вам что-нибудь полезное. Конечно, это вызывает следующий вопрос: почему у дженериков нет нижних границ?

Для более углубленного ответа смотрите: When to use generic methods and when to use wild-card? и http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ203

+0

Спасибо, я понял – Fio

+4

Стоит также отметить, что это не просто на путях. С помощью 'List ' вы можете написать 'E obj = list.get (0)', но с 'List ' вы можете писать 'Object obj = list.get (0)'. – yshavit

+0

@Jens schauder. И то и другое ? и E позволяет использовать только объекты-методы для объекта. И можно добавить в список и не может сделать то же самое со списком . Тогда зачем нам нужен список ? –

1

Список как параметр tuype говорит, что параметр должен быть списком элементов с любым типом объекта. Кроме того, вы можете связать параметр E, чтобы объявить ссылки на элементы списка внутри тела функции.

Список как тип параметра имеет одинаковую семантику, за исключением того, что нет способа объявить ссылки на элементы в списке, кроме как использовать Object. Другие сообщения дают дополнительные тонкие отличия.

+0

Извините, у меня есть ошибка в моем вопросе, и я редактировал вопрос. – Fio

+0

@Gene вы можете объяснить, почему во втором параметре функции должен быть список объектов, которые происходят из String. Поскольку параметр не определяет его тип объекта «Список список», поэтому я думаю, что он может содержать любой тип объекта вместе со строкой. –

+0

Вместо того, чтобы говорить «первый» или «второй», включите соответствующий код, чтобы избежать путаницы (и редактирует OP). –

5

Generics делает коллекцию более безопасной.

List<E>: E здесь является тип параметра, который может быть использован для определения типа содержимого списка, но был No способ проверить, что было содержание в течение runtime.

Generics are checked only during compilation time. 

<? extends String>: Это было специально встраивать в Java, чтобы справиться с этой проблемой, которая была с типом параметра. "? extends String" означает, что этот список может быть

objects which IS-A String. 

Для например:

класса животных класса Dog расширяет животные Тайгер класс расширяет ANIMAL

Таким образом, использование "public void go(ArrayList<Animal> a)" будет NOT accept собаки или тигра как его содержание, но животные.

"public void go(ArrayList<? extends Animal> a)" это, Что нужно сделать ArrayList take in Dog and Tiger type.

Проверить ссылки в Head First Java.

+0

Могу я использовать? как показано в приведенном выше списке Список ? то какие типы объектов могут хранить –

+0

"?" используется для того, чтобы сборник мог принимать аргументы вызываемого метода, который расширяет тип, упомянутый здесь «». –

+0

Список список в этом примере пользователь не определил тип ... то какой тип объекта он примет? –

1

Первая функция, которая принимает параметр, который должен быть список элементов типа E.

второй пример типа не определен

List<?> list 

так что вы можете передать список любого типа объектов.

0

(Начиная с вашего редактирования) Эти две сигнатуры функций имеют одинаковую силу с внешним кодом - они оба принимают любой аргумент List. Подстановочный знак эквивалентен параметру типа, который используется только один раз.

1

Я обычно объясняют разницу между < E> и < ?> путем сравнения с логическими квантификациями, то есть универсальной квантификацией и экзистенциальной количественной оценкой.

  • соответствует "FORALL Е, ..."
  • соответствует «существует нечто (обозначаемое), так что ...."

Таким образом, оба из следующих общих деклараций метода

function 1:

public static <E> void funct1 (List<E>; list1) { 

} 

function 2:

public static void funct2(List<?> list) { 

} 

означает, что для всех классов типа E, мы define funct1, и, для некоторых существует класс, обозначенный < ?>, мы dfine funct1.

0

В дополнение к этим различиям, упомянутых выше, есть также дополнительная разница: Вы можете явно указать аргументы типа для вызова универсального метода:

List<Apple> apples = ... 
ClassName.<Banana>funct2(apples); // for some reason the compiler seems to be ok 
           // with type parameters, even though the method has none 

ClassName.<Banana>funct1(apples); // compiler error: incompatible types: List<Apple> 
            //     cannot be converted to List<Banana> 

(ClassName это имя класса, содержащего методы.)

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