Почему родовой класс классов Java стирает все генерические объекты для объекта, когда параметры типа не указаны?

Если у меня есть класс:

public class GenericClass<TBlah extends Number> {
    public List<String> getList() {
        return null;
    }
}

Когда я пытаюсь использовать этот метод из другого класса:

public class OtherClass {
    public void test() {
        GenericClass a = null;
        for (String s : a.getList()) {

        }
    }
}

Почему a.getList() возвращает a List<Object>, пока не измените строку над циклом for на:

GenericClass<Number> a = null;

В какой момент a.getList() возвращает a List<String> как он должен делать?

Изменить: я не понимаю, почему контракт, указанный getList(), должен быть затронут, как я объявляю свою переменную 'a'. getList() всегда возвращает a List<String>, не имеет значения, что TBlah.

Ответ 1

Потому что так работают дженерики. Не забывайте, что перед дженериками, когда вы объявили List, это был список Object. Ожидалось, что вы станете/получите Object, и вас заставили бросить, чтобы получить ваш объект с правильным типом. На самом деле это все еще есть список Object во время выполнения.

Generics - это способ для компилятора гарантировать, что вы вводите безопасность во время компиляции, если у вас нет предупреждений. Во время выполнения нет List<String>. Существует только List. Компилятор ставит для вас автоматический выбор, поэтому вы можете в своем коде писать String s = list.get(i) без кастования.

Когда вы объявляете GenericClass a, вы объявляете необработанный тип (вы должны получить предупреждение об этом), поэтому компилятор не имеет способа узнать, какой тип должен возвращаться a.getList(). Поэтому он использует Object. Когда вы объявляете GenericClass<Number> a = null;, теперь компилятор знает, какого типа ожидать для a.getList() и использует желаемый.

Изменить: Следует уточнить, что компилятор может знать, чего ожидать, только если вы соблюдаете контракт подписи (т.е. как в случае с GenericClass<Number>). Если вы не соблюдаете договор (т.е. Используете необработанный тип, который не соответствует extends Number), то контракт больше не применяется. Компилятор ведет себя так, как будто информация о типе не присутствует. Не забывайте, что компилятор также должен поддерживать обратную совместимость с кодом, который был создан в эпоху до генериков.

Ответ 2

Вам нужен владелец места/ссылка на список для ссылки, и ссылка должна быть одного типа. Например,

Список <String> x = a.getList();