Объединение необработанных типов и общих методов
Вот вопрос, этот первый список кода компилируется просто отлично (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>
не имеет никакого отношения к списку (т.е. <E>
).
Я понятия не имею, как использование необработанного типа влияет на оценку параметризованных методов с использованием независимых параметров типа.
- У кого-нибудь есть идеи, почему этот код не компилируется?
- Кто-нибудь знает какие-либо ссылки, где это поведение задокументировано?
5 ответов
Это не совсем то, что вы ожидаете, но если вы ссылаетесь на родовой класс в необработанном виде, вы теряете возможность использовать дженерики любым способом для членов экземпляра. Это не ограничивается общими методами, проверьте это:
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) M необработанного типа C, который не унаследован от его суперклассов или суперинтерфейсов, является необработанным типом, который соответствует стиранию его типа в общем объявлении, соответствующем C.
Когда вы не используете обобщенный тип, компилятор обрабатывает его как необработанный тип, и, следовательно, каждый универсальный тип становится Object
и поэтому вы не можете пройти String[]
потому что это нужно Object[]
Так вот сделка - если вы используете
List l = new ArrayList<String>();
Вы используете необработанный тип, и все его элементы экземпляра заменяются его аналогами стирания. В частности, каждый параметризованный тип, появляющийся в объявлении метода экземпляра, заменяется его необработанным аналогом. См. JLS 4.8 для деталей.
Это самое близкое описание, которое я нашел в спецификации, чтобы описать это наблюдаемое поведение:
Тип конструктора (§8.8), метода экземпляра (§8.8, §9.4) или нестатического поля (§8.3) M необработанного типа C, который не унаследован от его суперклассов или суперинтерфейсов, является стиранием его типа. в обобщенном объявлении, соответствующем C. Тип статического члена необработанного типа C совпадает с его типом в обобщенном объявлении, соответствующем C.
Это ошибка времени компиляции для передачи фактических параметров типа члену нестатического типа необработанного типа, который не унаследован от его суперклассов или суперинтерфейсов.
Исходя из вышеизложенного и наблюдаемого поведения, я думаю, что можно с уверенностью сказать, что все общие типы параметров удалены из необработанного типа. Конечно, использование необработанных типов не рекомендуется в устаревшем коде:
Использование необработанных типов допускается только в качестве уступки совместимости устаревшего кода. Использование необработанных типов в коде, написанном после введения универсальности в язык программирования Java, настоятельно не рекомендуется. Вполне возможно, что будущие версии языка программирования Java будут запрещать использование необработанных типов.
Может быть интересно, что такое поведение может быть "решено". Используйте два интерфейса: базовый неуниверсальный интерфейс и универсальный интерфейс. Тогда компилятор знает, что все функции базового неуниверсального интерфейса не являются универсальными, и будет обрабатывать их следующим образом.
Это поведение очень раздражает, если использовать потоки и цепочку функций, и поэтому я решаю это следующим образом.
Soution через наследование интерфейса:
public interface GenericInterface<X extends Y> extends BaseInterface
{
X getValue();
}
public interface BaseInterface
{
String getStringValue();
}
Теперь вы можете делать следующее без предупреждения и проблем:
GenericInterface object = ...;
String stringValue = object.getStringValue();
Не может быть никакого параметра типа, передаваемого в метод toArray(), так как ваш ArrayList является непараметрическим списком, он знает только, что он содержит объекты, вот и все. a.toArray()
всегда будет возвращать массив Object []. Опять же, вы должны бросить его (String[])
(со всеми опасностями в нем), если вы хотите указать, что он содержит определенный тип String.