#java #generics
#java #дженерики
Вопрос:
Давайте иметь следующую иерархию классов:
public class MyType {
}
public class MySuperclass<T extends MyType> {
protected Map<String, String> myMap = new TreeMap<String, String>();
protected String myMethod(String s) {
return myMap.get(s);
}
}
public class MySubclass extends MySuperclass {
@Override
protected String myMethod(String s) {
return myMap.get(s); // <-- compilation error
}
}
Почему возникает ошибка компиляции в переопределенном методе MySubclass
?
Сообщение об ошибке: «Несоответствие типов: невозможно преобразовать из объекта в строку».
Интересно то, что ошибка компиляции исчезает, если я определяю тип класса generics для MySuperclass
in MySubclass
definition:
public class MySubclass extends MySuperclass<MyType> {
@Override
protected String myMethod(String s) {
return myMap.get(s);
}
}
Может кто-нибудь объяснить это поведение? Я бы счел это ошибкой компилятора Java.
Я использую jdk1.6.0_24.
Ответ №1:
Это не ошибка. Расширяя MySuperclass
вместо MySuperclass<MyType>
, вы расширяете необработанный тип MySuperclass
, что означает, что myMap
он также будет иметь тип Map
вместо Map<String, String>
.
Комментарии:
1. точно. Это была одна из «головоломок» Блоха.
2. Но что общего у generic definition of
MySuperclass**<MyType>**
с generics ofMap**<String, String>** myMap
? Это два разных определения дженериков, между которыми нет «связи». Я не понимаю.3. @miso JLS объясняет это очень подробно
4. Да, это странно. Но именно так работает Java — необработанные типы полностью необработаны.
5. @Thilo: имейте в виду, что необработанные типы существуют только для обратной совместимости. Если вы используете необработанный тип, то компилятор предполагает, что ваш код ничего не знает о дженериках. Это необходимо, чтобы иметь возможность улучшать существующие библиотеки с помощью дженериков, не нарушая старый непатентованный код.
Ответ №2:
Это действительно неразумно. Это можно считать ошибкой в дизайне. Основной причиной является решение сохранить обратную совместимость с generified collection API, вместо того, чтобы сохранить старый и внедрить новый generified API. Это решение технически бессмысленно, и их объяснения смехотворны. Реальная причина этого, вероятно, в том, что Sun была вынуждена выпустить Java5, но у нее не хватило ресурсов, поэтому они выбрали простой путь (стирание). Итак, мы здесь, полностью облажались. Эта система ублюдочного типа не только сама по себе является проблемой, но и большим препятствием для внедрения любой новой функции.
Ответ №3:
если Foo является подтипом (подклассом или подинтерфейсом) Bar , а G является некоторым общим объявлением типа, это не тот случай, когда G является подтипом G.
Вы можете сослаться http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf (для получения дополнительной информации)