8

java記述子と内部クラスの署名との間の不一致の仕様に理由があるかどうかを理解しようとしています。(ここではクラス ファイルの内容を直接見ていますが、javap を使用して説明しています)。

(nbこれをJDK 1.6.0_33と1.7.0_05で試しましたが、Java 7のjavapで表示すると同じ問題があります.Java 6のjavapは、以下のSeanの回答によると、一般的な署名情報を表示していないようです。)

更新:議論してくれてありがとう - 私の見解は

  • 記述子 (一般的な情報を含まない) は正しいです。
  • 署名 (メソッドの属性であり、一般的な情報が含まれています) が正しくありません。<init> メソッドの SIGNATURE に関連する ConstPool エントリは、「 ConstantUTF8[(Ljava/util/list<TE;>)V] 」です。
  • Java 6 の Javap は署名ではなく、記述子だけを調べます。(私の推測!)

誰かが疑問に思うかもしれませんが、私は 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;
    }....

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

public 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に配置しました。

以下のSeanの回答を踏まえて、Java 6でjavapを使用してみましたが、両方で同じ出力が見られましたが、一般的な情報はありませんでした.

1.7.0_05-b06 で javap を使用して得られる正確な出力は次のとおりです。

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       
}
4

2 に答える 2

1

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の1つは、出力をJavaのように配置しようとすることです。おそらく、これはJDK7で導入された新機能であり、コンパイラーが追加したパラメーターを非表示にすることで、逆アセンブルされたジェネリックスをJavaのように見せようとします。残念ながら、これによりJavap(さらに)は実際にそこにあるものを確認するのに役に立たなくなります。

面白いキャッチ!

于 2013-02-28T13:21:20.107 に答える
1

上記のコードと 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 ?)。

それ以外は、私の出力はほとんど同じです。あなたが見ているものを説明するかもしれないコードに他の違いはありますか? コンパイル プロセスとクラス ファイルについて私が知っていることからすると、出力に違いが見られるとは思えません。

良い質問です。なぜこれが表示されるのか興味を持ってください。

于 2013-02-28T11:30:47.093 に答える