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