Несоответствие внутреннего класса Java между дескриптором и атрибутом подписи? (файл класса)

Я пытаюсь понять, есть ли в спецификации причина несоответствия между дескрипторами java и подписями для внутренних классов. (Здесь я смотрю непосредственно на содержимое файлов классов, но для иллюстрации использую javap).

( NB я пробовал это на JDK 1.6.0_33 и 1.7.0_05, у обоих есть одна и та же проблема при просмотре с javap из Java 7 - javap java 6, похоже, не показывает никакой общей информации о подписи, согласно ответу Шона ниже.)

Обновление: спасибо тем, кто обсуждает - мое мнение

  • Дескриптор (который не содержит общей информации) правильный.
  • Подпись (которая является атрибутом метода и содержит общую информацию) неверна. Соответствующая запись ConstPool для SIGNATURE метода ‹init›: «ConstantUTF8[(Ljava/util/list‹TE;›)V]»
  • Javap в Java 6 не смотрит на подпись, только на дескриптор. (Моя догадка!)

Если кому-то интересно, я наткнулся на это, не используя JAVAP, просто просматривая файлы классов, я использую javap только для того, чтобы показать это. (поэтому вряд ли это ошибка javap).

Учитывать:

public class InnerClassTest1 {

  public int getX() {
    return new Inner1(new ArrayList<String>()).getX(4);
  }

  public class Inner1 {
    private final List arg;

    public Inner1(List arg) {
        this.arg = arg;
    }....

vs

public class InnerClassTest2 {

   public int getX() {
      return new Inner1(new ArrayList<String>()).getX(4);
   }

   public class Inner1<E> {
    private final List<E> arg;

    public Inner1(List<E> arg) {
        this.arg = arg;
    }.....

Если вы посмотрите на вывод javap -cs для внутренних классов, они удивительно отличаются!

public org.benf.cfr.tests.InnerClassTest1$Inner1(org.benf.cfr.tests.InnerClassTest1, java.util.List); Подпись: (Lorg/benf/cfr/tests/InnerClassTest1;Ljava/util/List;)V

vs

общедоступный org.benf.cfr.tests.InnerClassTest2$Inner1(java.util.List‹E›); Подпись: (Lorg/benf/cfr/tests/InnerClassTest2;Ljava/util/List;)V

... в том, который использует дженерики, отсутствует неявный параметр для внешнего класса! (он правильно присутствует в InnerClassTest1).

Я не могу найти ничего в документации файла класса, чтобы объяснить это - кто-нибудь знает, почему это может быть?

Спасибо!

Ли.


Обновлять -

Я разместил файлы примеров по адресу http://www.benf.org/files/innerClassTest.tgz

Учитывая приведенный ниже ответ Шона, я попытался использовать javap на java 6 и увидел одинаковый вывод для обоих без общей информации - это заставляет меня поверить, что javap java 6 не отображает полную информацию о подписи?

Точный вывод, который я получаю, используя javap на 1.7.0_05-b06,

public class org.benf.cfr.tests.InnerClassTest2$Inner1<E> {
 final org.benf.cfr.tests.InnerClassTest2 this$0;
 Signature: Lorg/benf/cfr/tests/InnerClassTest2;

public org.benf.cfr.tests.InnerClassTest2$Inner1(java.util.List<E>);
 Signature: (Lorg/benf/cfr/tests/InnerClassTest2;Ljava/util/List;)V
 Code:
   0: aload_0       
   1: aload_1       
   2: putfield      #1                  // Field this$0:Lorg/benf/cfr/tests/InnerClassTest2;
   5: aload_0       
   6: invokespecial #2                  // Method java/lang/Object."<init>":()V
   9: aload_0       
  10: aload_2       
  11: putfield      #3                  // Field arg:Ljava/util/List;
  14: return        

public int getX(int);
  Signature: (I)I
  Code:
   0: iconst_2      
   1: ireturn       
}

person lab27    schedule 28.02.2013    source источник
comment
Пожалуйста, укажите детали JDK   -  person default locale    schedule 28.02.2013
comment
Не могли бы вы опубликовать фактические файлы классов? Это, по крайней мере, сузило бы его до проблемы компилятора и проблемы с дизассемблером.   -  person Antimony    schedule 28.02.2013
comment
Да, я изменил вопрос с этим. Они здесь — benf.org/files/innerClassTest.tgz . В соответствии с моей поправкой также интересно, что javap (под java 6), похоже, игнорирует здесь общую информацию о подписи.   -  person lab27    schedule 28.02.2013
comment
Фактические дескрипторы в порядке, но javap их не печатает. Должна быть ошибка в Javap.   -  person Antimony    schedule 28.02.2013
comment
Если подумать, это, вероятно, преднамеренная функция Javap. Смотрите мой ответ.   -  person Antimony    schedule 28.02.2013
comment
@lab27 - спасибо за обновление - все очень интересно   -  person Sean Landsman    schedule 28.02.2013


Ответы (2)


Используя приведенный выше код и используя JDK 1.6.0_33, я получаю следующий вывод:

src\test>javap -c -s InnerClassTest1$Inner1
Compiled from "InnerClassTest1.java"
public class test.InnerClassTest1$Inner1 extends java.lang.Object{
final test.InnerClassTest1 this$0;
  Signature: Ltest/InnerClassTest1;

public test.InnerClassTest1$Inner1(test.InnerClassTest1, java.util.List);
  Signature: (Ltest/InnerClassTest1;Ljava/util/List;)V
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield        #1; //Field this$0:Ltest/InnerClassTest1;
   5:   aload_0
   6:   invokespecial   #2; //Method java/lang/Object."<init>":()V
   9:   aload_0
   10:  aload_2
   11:  putfield        #3; //Field arg:Ljava/util/List;
   14:  return

public int getX(int);
  Signature: (I)I
  Code:
   0:   iload_1
   1:   ireturn
}

src\test>javap -c -s InnerClassTest2$Inner1
Compiled from "InnerClassTest2.java"
public class test.InnerClassTest2$Inner1 extends java.lang.Object{
final test.InnerClassTest2 this$0;
  Signature: Ltest/InnerClassTest2;

public test.InnerClassTest2$Inner1(test.InnerClassTest2, java.util.List);
  Signature: (Ltest/InnerClassTest2;Ljava/util/List;)V
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield        #1; //Field this$0:Ltest/InnerClassTest2;
   5:   aload_0
   6:   invokespecial   #2; //Method java/lang/Object."<init>":()V
   9:   aload_0
   10:  aload_2
   11:  putfield        #3; //Field arg:Ljava/util/List;
   14:  return

public int getX(int);
  Signature: (I)I
  Code:
   0:   iload_1
   1:   ireturn

}

и единственная разница в том, что моя реализация (чтобы код компилировался):

    public int getX(int i) {
        return i;
    }

и тот факт, что имя вашего пакета, вероятно, отличается (org.benf.cfr.tests?).

В остальном, мой вывод почти такой же. Есть ли другие отличия в коде, которые могли бы объяснить то, что вы видите? Из того, что я знаю о процессе компиляции и файлах классов, я не ожидал увидеть разницу в выводе.

Хороший вопрос — интересно узнать, почему вы это видите

person Sean Landsman    schedule 28.02.2013
comment
Это очень интересно - возможно, это что-то особенное для моего окружения, хотя я понятия не имею, что это может быть! Я разместил свой /exact/ вывод, а также файлы классов ссылка здесь , если хотите посмотреть. - person lab27; 28.02.2013
comment
Интересно, делает ли javap java 6 (вы это используете?) что-то другое здесь, так как я не вижу никакой общей информации в выводе, который у вас есть для InnerClassTest2. - person lab27; 28.02.2013
comment
Я загрузил ваши файлы и сделал для них javap -c -p, и вывод идентичен приведенному выше (за исключением того, что пакет отличается) - так что похоже, что javap является виновником - person Sean Landsman; 28.02.2013
comment
Я использую javap -c -s ‹Имя класса›, и у меня есть следующая версия Java: версия java 1.6.0_33 Java(TM) SE Runtime Environment (сборка 1.6.0_33-b03) Java HotSpot(TM) 64-Bit Серверная ВМ (сборка 20.8-b03, смешанный режим) - person Sean Landsman; 28.02.2013
comment
re: общий вывод в javap - я даже не знал, что это возможно, учитывая, что я думал, что информация отсутствует после компиляции, но, учитывая ваш вывод, может быть, это так? Однако я был бы удивлен, поскольку дженерики не предназначались для изменения файлов классов, созданных в jdk5 и далее. - person Sean Landsman; 28.02.2013
comment
Я провел некоторое исследование, и кажется, что в классе сохраняется некоторая общая информация (узнал что-то новое!). Кроме того, я думаю, что jdk6 javap этого не показывает, а jdk7 показывает - можете ли вы проверить, что вы использовали jdk6 для обоих сравнений? возможно ли, что вы использовали jdk6 javap для 1 и jdk7 javap для другого (здесь просто предположение) - person Sean Landsman; 28.02.2013
comment
@Sean Generics (вместе с внутренними классами) сохраняются с помощью необязательных атрибутов. Они не влияют на фактическое выполнение байт-кода, но используются для отражения и компиляции с файлами классов. По сути, это комментарии в отношении JVM. - person Antimony; 28.02.2013
comment
Да, общая информация сохраняется как атрибуты класса/метода. В данном случае проблема заключается в том, что метод ‹init› имеет правильный дескриптор, но сигнатура (Ljava/util/List‹TE;›;)V . Я поправлю вопрос. - person lab27; 28.02.2013

Использование Javap на InnerClassTest2$Inner1 дает

Compiled from "InnerClassTest2.java"
public class org.benf.cfr.tests.InnerClassTest2$Inner1<E> {
  final org.benf.cfr.tests.InnerClassTest2 this$0;
  public org.benf.cfr.tests.InnerClassTest2$Inner1(java.util.List<E>);
  public int getX(int);
}

Разборка с Кракатау дает

.version 51 0
.source InnerClassTest2.java
.class super public org/benf/cfr/tests/InnerClassTest2$Inner1
.super java/lang/Object

.field final private arg Ljava/util/List;
.field synthetic final this$0 Lorg/benf/cfr/tests/InnerClassTest2;

.method public <init> : [_13]
    .limit stack 2
    .limit locals 3
    aload_0
    aload_1
    putfield org/benf/cfr/tests/InnerClassTest2$Inner1 this$0 Lorg/benf/cfr/tests/InnerClassTest2;
    aload_0
    invokespecial java/lang/Object <init> ()V
    aload_0
    aload_2
    putfield org/benf/cfr/tests/InnerClassTest2$Inner1 arg Ljava/util/List;
    return
.end method

.method public getX : (I)I
    .limit stack 1
    .limit locals 2
    iconst_2
    ireturn
.end method

.const [_13] = Utf8 (Lorg/benf/cfr/tests/InnerClassTest2;Ljava/util/List;)V

Как видите, вывод Krakatau показывает, что дескриптор на самом деле правильный, но по какой-то причине Javap его не отображает. Одна особенность Javap заключается в том, что он пытается упорядочить вывод так, чтобы он больше походил на Java. Возможно, это новая функция, представленная в JDK7, которая пытается сделать дизассемблированные дженерики более похожими на Java, скрывая параметры, добавленные компилятором. К сожалению, это делает Javap (даже более) бесполезным для просмотра того, что там на самом деле.

Интересный улов!

person Antimony    schedule 28.02.2013