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>
не восстанавливается.