Объединение сырых типов и общих методов
Вот вопрос, этот первый список кодов компилируется просто отлично (JDK 1.6 | JDK 1.7):
ArrayList a = new ArrayList(); String[] s = a.toArray(new String[0]);
Однако, если я объявляю ссылку List
как необработанный тип:
ArrayList a = new ArrayList(); String[] s = a.toArray(new String[0]);
Я получаю ошибку компилятора, указывающую, что String[]
требуется, но Object[]
был найден.
- Общие методы Java в classах дженериков
- Невозможно преобразовать из списка в список <список >
- Что такое сырой тип и почему мы не должны его использовать?
Это означает, что мой компилятор интерпретирует общий метод как возвращающий Object[]
несмотря на получение в качестве аргумента String[]
.
Я дважды проверял подпись метода toArray(myArray)
:
T[] toArray(T[] a);
Поэтому это параметризованный метод, параметр типа имеет никакого отношения к classу List (т.е.
).
Я понятия не имею, как использование необработанного типа влияет на оценку параметризованных методов с использованием независимых параметров типа.
- Кто-нибудь знает, почему этот код не компилируется?
- Кто-нибудь знает какую-либо ссылку, где это поведение документировано?
Это не совсем то, что вы ожидаете, но если вы ссылаетесь на общий class в необработанном виде, вы теряете возможность использовать дженерики каким-либо образом, например, для членов. Это также не ограничивается общими методами, проверьте это:
public class MyContainer { public List strings() { return Arrays.asList("a", "b"); } } MyContainer container = new MyContainer(); List strings = container.strings(); //gives unchecked warning!
Это соответствующая часть JLS ( 4.8 ):
Тип конструктора (§8.8), метод экземпляра (§8.4, п. 9.4) или нестатическое поле (§8.3) М необработанного типа С, который не унаследован от его суперclassов или суперинтерфейсов, является необработанным типом, который соответствует к стиранию его типа в общей декларации, соответствующей C.
Когда вы не используете generics, компилятор рассматривает его как необработанный тип, и поэтому каждый родовой тип становится Object
и поэтому вы не можете передать String[]
поскольку ему нужен Object[]
Итак, вот сделка – если вы используете
List l = new ArrayList();
Вы используете raw- тип, и все его экземпляры заменяются его аналогами стирания. В частности, каждый параметризованный тип, появляющийся в объявлении метода экземпляра, заменяется его исходной копией. Подробнее см. JLS 4.8.
Это самое близкое описание, которое я нашел в спецификации для описания этого наблюдаемого поведения:
Тип конструктора (§8.8), метод экземпляра (§8.8, §9.4) или нестатического поля (§8.3) М необработанного типа С, который не унаследован от его суперclassов или суперинтерфейсов, – это стирание его типа в общем объявлении, соответствующем C. Тип статического члена необработанного типа C такой же, как и его тип, в общем объявлении, соответствующем C.
Это ошибка времени компиляции для передачи параметров фактического типа члену нестатического типа необработанного типа, который не унаследован от его суперclassов или суперинтерфейсов.
Исходя из вышеизложенного и наблюдаемого поведения, я думаю, что можно с уверенностью сказать, что все типовые типы параметров удаляются из необработанного типа. Разумеется, использование самих типов raw не рекомендуется в коде, не принадлежащем устаревшему:
Использование типов raw допускается только в качестве уступки совместимости устаревшего кода. Использование необработанных типов в коде, написанном после введения родословности в язык программирования Java, настоятельно не рекомендуется. Возможно, что будущие версии языка программирования Java будут запрещать использование необработанных типов .
Может быть интересно, что такое поведение можно «решить». Используйте два интерфейса, базовый не общий интерфейс и общий интерфейс. Тогда компилятор знает, что все функции базового не общего интерфейса не являются общими и будут относиться к ним так.
Такое поведение очень раздражает, если вы используете streamи и цепочки функций, и поэтому я решаю это как следующее.
Soution через наследование интерфейса :
public interface GenericInterface extends BaseInterface { X getValue(); } public interface BaseInterface { String getStringValue(); }
Теперь вы можете делать следующее без предупреждения или проблем:
GenericInterface object = ...; String stringValue = object.getStringValue();
Параметр типа не передается в метод toArray (), так как ваш ArrayList является непараметрированным списком, он знает только, что он содержит объекты, вот и все. a.toArray()
всегда возвращает массив Object []. Опять же, вы должны отдать его (String[])
(со всеми опасностями в нем), если вы хотите указать, что он содержит определенный тип String.