Почему коллекция не просто рассматривается как коллекция <?>

Рассмотрим следующий метод API, взятый из интерфейса Shiro org.apache.shiro.subject.PrincipalCollection но, вероятно, присутствующий и в других библиотеках:

Collection fromRealm(String realmName);

Да, даже в наши дни все еще существуют библиотеки, использующие raw-типы, возможно, для сохранения совместимости до Java 1.5?!

Если я сейчас хочу использовать этот метод вместе с потоками или дополнительными функциями, как это:

principals.fromRealm(realmName).stream().collect(Collectors.toSet());

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

Затмение:

Тип безопасности: метод collect (Collector) относится к необработанному типу Stream. Ссылки на универсальный тип Stream <T> должны быть параметризованы

Javac:

Примечание. GenericsTest.java использует непроверенные или небезопасные операции.

Поскольку я не могу изменить сигнатуру метода API, чтобы избавиться от этого предупреждения, я могу либо аннотировать с помощью @SuppressWarnings("unchecked") либо просто привести к Collection<?> @SuppressWarnings("unchecked") образом:

((Collection<?>) principals.fromRealm(realmName)).stream().collect(Collectors.toSet());

Поскольку этот набор, конечно, всегда работает, мне интересно, почему компиляторы не просто обрабатывают Collection как Collection<?> Но предупреждают об этой ситуации. Добавление аннотации или приведений не улучшает код на один бит, но снижает читаемость или может даже скрывать действительные действительные предупреждения об использовании непараметризованных типов.

Ответ 1

Причина довольно проста:

Вы можете читать Object из Collection<?> же, как из Collection. Но вы не можете добавить Object в Collection<?> (Это запрещено компилятором), тогда как в Collection это можно сделать.

Если после выпуска Java 5 компилятор переведет каждую Collection в Collection<?>, То ранее написанный код больше не будет компилироваться и, следовательно, нарушит обратную совместимость.

Ответ 2

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

List raw = new ArrayList();
raw.add("");
raw.add(1);

На самом деле, в случае неограниченных наборов подстановочных знаков (List<?> wildcard = new ArrayList<String>()), вы ничего не можете добавить в список, кроме null (из документов Oracle):

Поскольку мы не знаем, что означает тип элемента c, мы не можем добавлять к нему объекты. Метод add() принимает аргументы типа E, типа элемента коллекции. Когда фактический параметр типа равен?, Он обозначает некоторый неизвестный тип. Любой параметр, который мы передаем для добавления, должен быть подтипом этого неизвестного типа. Поскольку мы не знаем, что это за тип, мы не можем ничего передать. Единственным исключением является null, который является членом каждого типа.

Ответ 3

Это может быть слишком основано на мнениях для SO, но я считаю, что смысл того, что компилятор предупреждает вас, а не молча предполагает, что Collection<?> Заключается в том, что использование необработанных типов - это то, что следует избегать, когда это возможно. Это не ошибка, потому что вы не всегда можете избежать этого, но это то, что не следует поощрять. Предупреждение препятствует использованию необработанных типов, что заставляет вас задаться вопросом, было ли необходимо конкретное использование. Молча относиться к этому как к Collection<?> Нет.

Ответ 4

Пример использования, который я могу придумать, почему Collection не рассматривается как Collection<?> Скажем, у нас есть экземпляр ArrayList

Теперь, если экземпляр имеет тип ArrayList<Integer> или ArrayList<Double> или ArrayList<String>, вы можете добавить только этот тип (проверка типа). ArrayList<?> Не эквивалентен ArrayList<Object>.

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

Еще одной причиной может быть обратная совместимость с Java-версией, у которой нет дженериков.

Ответ 5

Collection<?> Кричит:

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

В то время как Collection говорит:

Это все круто! Вы можете добавить что угодно, у меня нет ограничений.

Итак, почему компилятор не должен переводить Collection в Collection<?>? Потому что это поставило бы много ограничений.