Почему этот общий код Java не компилируется?
В этом упрощенном примере у меня есть общий класс и метод, который возвращает Map независимо от параметра type. Почему компилятор уничтожает типы на карте, когда я не указываю тип в содержащем классе?
import java.util.Map;
public class MyClass<T>
{
public Map<String, String> getMap()
{
return null;
}
public void test()
{
MyClass<Object> success = new MyClass<Object>();
String s = success.getMap().get("");
MyClass unchecked = new MyClass();
Map<String, String> map = unchecked.getMap(); // Unchecked warning, why?
String s2 = map.get("");
MyClass fail = new MyClass();
String s3 = fail.getMap().get(""); // Compiler error, why?
}
}
Я получаю эту ошибку компилятора.
MyClass.java:20: incompatible types
found : java.lang.Object
required: java.lang.String
String s3 = fail.getMap().get(""); // Compiler error
Ответы
Ответ 1
Получил это. На самом деле это не ошибка, как это ни странно.
Из раздел 4.8 (необработанные типы) JLS:
Тип конструктора (§8.8), (§8.8, §9.4) или нестатического поля (§8.3) М необработанного тип C, который не унаследован от суперклассов или суперинтерфейсов стирание его типа в родовом объявление, соответствующее C. тип статического члена необработанного типа C такой же, как и его тип в общая декларация, соответствующая С.
Итак, хотя подпись типа метода не использует параметры типа самого класса, введите erasure kicks in и подпись станет эффективно
public Map getMap()
Другими словами, я думаю, вы можете представить, что тип raw является тем же API, что и общий тип, но со всеми битами <X>
, удаленными извне (в API, а не в реализации).
EDIT: Этот код:
MyClass unchecked = new MyClass();
Map<String, String> map = unchecked.getMap(); // Unchecked warning, why?
String s2 = map.get("");
компилируется, потому что существует неявное, но непроверенное преобразование из исходного типа Map
в Map<String, String>
. Вы можете получить тот же эффект, сделав явное преобразование (которое ничего не делает во время выполнения) в последнем случае:
// Compiles, but with an unchecked warning
String x = ((Map<String, String>)fail.getMap()).get("");
Ответ 2
Хм... к сожалению, я не могу сказать, почему это терпит неудачу. Но я могу дать вам простой способ:
Измените тип fail
на MyClass<?>
, тогда он будет скомпилирован просто отлично.
Ответ 3
Очень интересный вопрос, и очень интересный ответ Джона Скита.
Я просто хочу добавить что-то о глупости или глупости этого поведения java-компилятора.
Я думаю, что компилятор предполагает, что , если вы не укажете параметр типа в классе generc, вы не можете (или не хотите) использовать любой параметр типа вообще. Вы могли бы использовать версию java раньше 5 или любить делать броски вручную.
Мне это не кажется таким глупым.
Ответ 4
После компиляции стираются стили.
Когда вы выполните:
Map<String, String> map = unchecked.getMap();
вы вынуждаете актерский состав из Map to Map < String, String > , и поэтому это предупреждение непроверено. Однако после этого вы можете сделать:
String s2 = map.get("");
потому что map имеет тип Map < String, String > .
Однако, когда вы делаете
String s3 = fail.getMap().get("");
вы не выполняете функцию fail.getMap() для чего-либо, поэтому он считается явно Map, а не Map < String, String > .
Что вы должны сделать в последнем, это что-то вроде:
String s3 = ((Map<String, String>fail.getMap()).get("");
который все равно будет вызывать предупреждение, но все равно будет работать.