Подтвердить что ты не робот

Что такое использование и точка несвязанных подстановочных знаков в Java?

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

4b9b3361

Ответ 1

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

Примитивным примером может быть следующее:

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>>.

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

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

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

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

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

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

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

Ответ 2

Если вам нужно выполнить проверку instanceof.

Вы не можете параметризовать следующее:

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

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

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

Ответ 3

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

Ответ 4

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

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

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

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

Constructor<?>[] getConstructors()

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

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

Ответ 5

Использование неограниченных групповых символов имеет смысл, AFAIK, при обертке старого кода, который не использует generics, в основном Collections.

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

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

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

Ответ 6

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

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

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

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

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