Java Reflection, почему аргументы типа супертипа сохраняются во время выполнения

Итак, у java есть стирание типа, которое удаляет аргументы типа (во время выполнения не существует общих типов). Может ли кто-нибудь объяснить, почему аргументы типа супертипа сохраняются?

class StringList extends ArrayList<String> {
    public Type getType() {

        Type[] typeArguments = ((ParameterizedType) this.getClass()
                .getGenericSuperclass()).getActualTypeArguments();
        return typeArguments[0];
    }
}

Что это означает в основном:

new ArrayList<String>()// I cannot use reflection to find out the generified type String

но

new StringList()//I can use reflection to see the generic type String

Таким образом, стирание типа для обратной совместимости, но почему аргументы типа супертипа сохраняются? Обновлены ли метаданные для его поддержки?

Почему один, а не другой?

Ответы

Ответ 1

Это сделано для обеспечения безопасности типа времени компиляции. Если суперкласс StringList был удален до ArrayList, не было бы способа защитить от

StringList stringList = new StringList();
stringList.add(new Integer(123456));

Если тип остается в файле .class для StringList, компилятор может корректно отказаться от исходного кода выше, поскольку new Integer(123456) не соответствует типу String, ожидаемому ArrayList<String>#add(String).

Ответ 2

Я не мог найти удовлетворительного ответа на этот вопрос, но вот мой подход к нему. Класс StringList - это новый тип, подкласс ArrayList. Компилятор создает файл класса для этого нового типа и добавляет любую информацию, доступную во время компиляции. Однако new ArrayList<String>() не генерирует новый тип. Это связано с тем, что вы просто создаете экземпляр существующего родового типа, и нет места для хранения типа параметра вашего экземпляра.

Дополнительные данные, которые вы видите, хранятся в атрибуте Signature вашего класса StringList. Вы можете увидеть его, открыв файл .class в любом редакторе.

Ваш класс по-прежнему стирается, и у вас нет защиты от выполнения:

StringList sl = new StringList();
List l = sl;
l.add(new Integer(123)); // no compile or runtime error

Лучшим ответом может быть вопрос о том, как можно использовать эту дополнительную информацию (приложением или JVM.)

Ответ 3

В Java важно различать типы времени компиляции (как указано в исходном коде) и типы времени выполнения отдельных объектов.

Типы времени компиляции используются для компиляции и связывания (в Java связь происходит, когда файл класса загружается JVM). Поскольку вы можете скомпилировать (и связывать против) файлы классов, типы времени компиляции должны быть сохранены полностью в файлах классов.

Типы выполнения используются для проверки типов во время выполнения, как в инструкциях cast или instanceof. Их также можно запросить с помощью object.getClass(). Erasure означает, что аргументы типа отсутствуют в типах времени выполнения, т.е. JVM не отслеживает, какие аргументы типа создавался с помощью:

new ArrayList<String>().getClass() == new ArrayList<Integer>().getClass()

То есть, аргументы типа присутствуют (и могут быть запрошены) в типах времени компиляции, но не в типах времени выполнения.

Ответ 4

Определения типа типа в основном определяют тип. Пример:

class StringList extends ArrayList<String> {}

Так как <String> является частью определения StringList здесь, он может быть восстановлен во время выполнения. Альтернативно, если просто объявить a new ArrayList<String> аргумент <String> не восстанавливается.