Где универсальные типы хранятся в файлах классов Java? - PullRequest
15 голосов
/ 02 июня 2009

Мне хорошо известно, что универсальные типы стираются из кода Java при его компиляции. Какую информацию (атрибуты?) Используют 1,5+ JVM для реализации getGenericType и т. Д.?

Ответы [ 2 ]

30 голосов
/ 02 июня 2009

Они хранятся в 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 и не имеет интерфейсов.

14 голосов
/ 02 июня 2009

Обобщения Java действительно реализованы с помощью стирания типа , поэтому в байт-коде нет информации о типе.

Например, давайте рассмотрим два класса, которые объявляют поле 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. Итак, мы можем видеть, что обобщения действительно реализуются с помощью стирания типов.

Однако, если мы посмотрим на постоянный пул, мы сможем найти разницу.

Неуниверсальный постоянный пул:

  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;>;;

Поэтому информация из пула констант используется для указания того, что поле имеет универсальный тип.

...