Первая подпись гласит: 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
Если String ссылается на 'java.lang.String', я думаю, что funct2 довольно избыточен в подстановочных выражениях, поскольку' java.lang.String' является окончательным классом и не может быть расширен. – nhahtdh
Это странно, потому что технически вы не можете расширить String со своего последнего класса. –
однако нет ограничений на использование функции 2: поскольку она позволяет подклассы String (хотя это невозможно) или сам класс String ... – ria