Какова польза и смысл обобщенных шаблонов в Java? - PullRequest
26 голосов
/ 06 октября 2011

Я не понимаю, что такое использование несвязанных шаблонов подстановочных знаков. Связанные шаблоны с подстановочными знаками с верхней границей <? extends Animal> имеют смысл, потому что, используя полиморфизм, я могу работать с этим типом или коллекцией. Но какой смысл иметь дженерики, которые могут быть любого типа? Разве это не побеждает цель дженериков? Компилятор не находит никакого конфликта, и после стирания типа это будет выглядеть так, как если бы не использовались дженерики.

Ответы [ 6 ]

27 голосов
/ 06 октября 2011

Несвязанный тип может быть полезен, когда ваш метод на самом деле не заботится о реальном типе.

Примитивный пример будет таким:

public void printStuff(Iterable<?> stuff) {
  for (Object item : stuff) {
    System.out.println(item);
  }
}

Поскольку PrintStream.println() может обрабатывать все ссылочные типы (вызывая toString()), мы не заботимся , каково фактическое содержание этого Iterable.

И абонент может передать List<Number> или Set<String> или Collection<? extends MySpecificObject<SomeType>>.

Также обратите внимание, что отсутствие использования обобщений (которое вызывается с использованием необработанного типа) вообще имеет совершенно иной эффект: он заставляет компилятор обрабатывать весь объект , как если бы обобщения не существовали на все . Другими словами: игнорируется не только параметр типа класса, но и все параметры универсального типа в методах.

Другим важным отличием является то, что вы не можете добавить любое (не null) значение к Collection<?>, но можете добавить все объекты к необработанному типу Collection:

Это не скомпилируется, поскольку параметр типа c является неизвестным типом (= подстановочный знак ?), поэтому мы не можем предоставить значение, которое гарантированно будет назначено этому (кроме *) 1035 *, который присваивается всем ссылочным типам).

Collection<?> c = new ArrayList<String>();
c.add("foo");    // compilation error

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

Collection c = new ArrayList<String>();
c.add("foo");
c.add(new Integer(300));
c.add(new Object());

Обратите внимание, что компилятор предупредит , что вам не следует использовать необработанный тип, особенно по этой причине: он удаляет любые проверки типов, связанные с обобщениями.

6 голосов
/ 31 июля 2015

Когда вам нужно выполнить проверку instanceof.

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

Object value;
if (value instanceof List<String>) {
    // ...
}

Итак, вы делаете:

Object value;
if (value instanceof List<?>) {
    // ...
}
2 голосов
/ 31 июля 2015

Существуют (редкие) совершенно правильные варианты использования для несвязанных подстановочных знаков.SDK содержит некоторые из них.

Одним из примеров является метод, который выполняет определенное действие над списком любого вида и ничего не возвращает как rotate в Collections:

static void rotate(List<?> list, int distance)

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

Constructor<?>[] getConstructors()

Здесь даже невозможно использовать универсальный тип, поскольку по определению массив будет содержать другой конструктор.каждый со своим реальным классом.В отличие от этого, API действительно использует общую сигнатуру для получения одного единственного конструктора: Constructor<T> getConstructor(Class<?>... parameterTypes).

Вывод таков: даже если он в основном используется для совместимости со старым кодом, все еще есть места, где не привязанные универсальные шаблоныправильный путь.

1 голос
/ 25 сентября 2017

Позвольте мне перефразировать вопрос:

«В чем разница между List<Object> и List<?>

Ответ на этот вопрос таков: List<?> является более строгим. Это говорит нам о том, что у нас есть группа объектов типа некоторого , но этот тип не обязательно Object.

Поскольку мы не знаем, что это за тип, мы вообще не можем добавить его в список - все, что мы добавляем, может иметь неправильный тип. Фактически, мы не можем передать любой аргумент типа ? какому-либо методу, не только add().

С положительной стороны, когда мы указываем, что метод принимает List<?>, он может принимать List<String> или List<Integer> или любой другой List<>. List<Object> можно взять только List<Object>.

1 голос
/ 06 октября 2011

Хотя использование необработанных типов означает, что вы не знаете об обобщениях (потому что вы ленивы или код был написан много лет назад), использование <?> означает, что вы знаете об обобщениях и явно подчеркиваете, что ваш код может работать с любымвиды объектов.

0 голосов
/ 06 октября 2011

Использование неограниченных подстановочных знаков имеет смысл, AFAIK, только при переносе старого кода, в котором не используются универсальные шаблоны, в основном это Коллекции.

Если вы посмотрите на то, что вы можете сделать с таким универсальным, это в основном ничего . Если у вас есть коллекция, вы ничего не можете добавить, если вы попытаетесь что-то прочитать, вы всегда получите Object и так далее.

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

Какие методы и поля доступны / недоступны через ссылочную переменную параметризованного типа с подстановочными знаками? из Анжелика Лангерс Часто задаваемые вопросы по Generics Java может представлять интерес.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...