Ошибка Java generics?

#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 of Map**<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 (для получения дополнительной информации)