Где общие типы хранятся в файлах java-класса?
Мне хорошо известно, что общие типы стираются из кода Java при компиляции. Какая информация (атрибуты?) Использует 1.5+ JVM для реализации getGenericType
и т.д.?
Ответы
Ответ 1
Они хранятся в атрибутах Signature
; см. раздел 4.8.8 обновленная спецификация виртуальной машины Java, а также раздел 4.4.4 для формата сигнатуры типа поля.
Здесь пример с использованием javap -verbose java.util.Map
:
public interface java.util.Map
SourceFile: "Map.java"
Signature: length = 0x2
00 1E
[other attributes omitted]
Атрибут Signature
здесь указывает (если вы читаете это как big-endian, как и все целочисленные величины в формате файла JVM-класса): постоянное значение пула # 30 (30 = 0x1E). Поэтому давайте посмотрим:
const #30 = Asciz <K:Ljava/lang/Object;V:Ljava/lang/Object;>Ljava/lang/Object;;
Прочтите это в контексте грамматики, указанной в 4.4.4. Таким образом, это использует два типа параметров, K extends java.lang.Object
и V extends java.lang.Object
. Сам тип (Map
) также расширяет класс java.lang.Object
и без интерфейсов.
Ответ 2
Генераторы Java действительно реализованы типа erasure, поэтому в байт-коде нет информации о типе.
Например, рассмотрим два класса, которые объявляют поле List
, одно в общем, а другое в неэквивалентной форме:
class NonGeneric {
List list;
}
и
class Generic {
List<String> list;
}
В обоих случаях результирующий байт-код выглядит следующим образом:
Code:
Stack=3, Locals=1, Args_size=1
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: aload_0
5: new #2; //class java/util/ArrayList
8: dup
9: invokespecial #3; //Method java/util/ArrayList."<init>":()V
12: putfield #4; //Field list:Ljava/util/List;
15: return
Нет ссылки на тип String
, используемый в ArrayList
и List
. Итак, мы видим, что generics действительно реализуется стиранием типа.
Однако, если мы посмотрим на постоянный пул, мы можем найти разницу.
Неоригинальный пул констант:
Constant pool:
const #1 = Method #6.#15; // java/lang/Object."<init>":()V
const #2 = class #16; // java/util/ArrayList
const #3 = Method #2.#15; // java/util/ArrayList."<init>":()V
const #4 = Field #5.#17; // NonGeneric.list:Ljava/util/List;
const #5 = class #18; // NonGeneric
const #6 = class #19; // java/lang/Object
const #7 = Asciz list;
const #8 = Asciz Ljava/util/List;;
const #9 = Asciz <init>;
const #10 = Asciz ()V;
// snip the rest //
Общий пул констант:
Constant pool:
const #1 = Method #6.#17; // java/lang/Object."<init>":()V
const #2 = class #18; // java/util/ArrayList
const #3 = Method #2.#17; // java/util/ArrayList."<init>":()V
const #4 = Field #5.#19; // Generic.list:Ljava/util/List;
const #5 = class #20; // Generic
const #6 = class #21; // java/lang/Object
const #7 = Asciz list;
const #8 = Asciz Ljava/util/List;;
const #9 = Asciz Signature;
const #10 = Asciz Ljava/util/List<Ljava/lang/String;>;;
const #11 = Asciz <init>;
const #12 = Asciz ()V;
// snip the rest//
Как видно, в классе Generic
мы можем видеть, что в пуле констант есть две дополнительные константы #9
и #10
, в которой упоминается, что List
имеет общий тип String
.
(И включение новых знаний, которые я узнал из Криса Джеттера-Юнга)
Если вы посмотрите на дизассемблирование файла класса, есть ссылка на константу # 10 прямо перед Code: block
класса Generic
:
java.util.List list;
Signature: length = 0x2
00 0A
Шестнадцатеричное значение 0A
равно 10
в десятичном значении, которое ссылается на константный пул #10
:
const #10 = Asciz Ljava/util/List<Ljava/lang/String;>;;
Следовательно, информация из пула констант используется, чтобы указать, что поле имеет общий тип.