Я новичок в Generic, и мой вопрос: какая разница между двумя функциями:
Функция 1:
public static <E> void funct1 (List<E> list1) {
}
функция 2:
public static void funct2(List<?> list) {
}
Спасибо.
Я новичок в Generic, и мой вопрос: какая разница между двумя функциями:
Функция 1:
public static <E> void funct1 (List<E> list1) {
}
функция 2:
public static void funct2(List<?> list) {
}
Спасибо.
Первая подпись говорит: list1 - это список Es.
Вторая подпись говорит: list - это список экземпляров какого-то типа, но мы не знаем тип.
Различие становится очевидным, когда мы пытаемся изменить метод, поэтому он принимает второй аргумент, который следует добавить в список внутри метода:
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
не дает вам ничего полезного. Конечно, это вызывает следующий вопрос: почему нет дженериков с нижними границами?Для более подробного ответа см.: Когда использовать общие методы и когда использовать wild-card? и http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ203
Generics делает коллекцию более безопасной.
List<E>
: E здесь - параметр типа, который может использоваться для определения типа содержимого списка, но существует способ No
проверить, что было содержанием во время runtime
.
Generics are checked only during compilation time.
<? extends String>
: Это было специально встроено в java, чтобы справиться с проблемой, которая была с параметром типа. "? extends String"
означает, что этот список может иметь
objects which IS-A String.
Например,
Класс животных Класс собаки расширяет животных Класс тигра расширяет Animal
Таким образом, используя "public void go(ArrayList<Animal> a)"
будет NOT accept
Собака или Тигр в качестве своего содержимого, но Animal.
"public void go(ArrayList<? extends Animal> a)"
необходимо сделать ArrayList take in Dog and Tiger type.
Проверьте ссылки в Head First Java.
Список как параметр tuype говорит, что параметр должен быть списком элементов с любым типом объекта. Кроме того, вы можете привязать параметр E
для объявления ссылок на элементы списка внутри тела функции.
Список как тип параметра имеет одинаковую семантику, за исключением того, что не существует способа объявить ссылки на элементы в списке, кроме как использовать Object
. Другие сообщения дают дополнительные тонкие различия.
Первая - это функция, которая принимает параметр, который должен быть списком элементов типа E.
второй тип примера не определен
List<?> list
чтобы вы могли передавать список объектов любого типа.
Я обычно объясняю разницу между < E > и < ? > сопоставлением с логическими квантификациями, то есть универсальной количественной оценкой и экзистенциальной количественной оценкой.
Следовательно, обе следующие объявления обобщенного метода
function 1
:
public static <E> void funct1 (List<E>; list1) {
}
function 2
:
public static void funct2(List<?> list) {
}
означает, что для всех типов классов E мы определяем funct1
, а для какого-либо существующего класса, обозначенного символом ?, мы dfine funct1
,
(Так как вы редактируете) Эти две сигнатуры функций имеют одинаковую силу с внешним кодом - оба они принимают любой аргумент List
. Подстановочный знак эквивалентен параметру типа, который используется только один раз.
В дополнение к упомянутым выше различиям также существует дополнительная разница: вы можете явно задать аргументы типа для вызова общего метода:
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
- это имя класса, содержащего методы.)