I'm trying to understand if there's a reason in the spec for the discrepany between java descriptors and signatures for inner classes. (I'm looking directly at the content of the class files here, but I use javap to illustrate).
( n.b. I have tried this on JDK 1.6.0_33 and 1.7.0_05, both have the same issue when viewed with javap from Java 7 - java 6's javap doesn't seem to show any generic signature info, as per Sean's answer below. )
Update : Thanks to those discussing - my take is
- The descriptor (which doesn't contain generic information) is correct.
- The signature (which is an attribute of the method, and does contain generic information) is incorrect. The relevant ConstPool entry for SIGNATURE of the <init> method is " ConstantUTF8[(Ljava/util/list<TE;>)V] "
- Javap in Java 6 doesn't LOOK at the signature, just the descriptor. (My guess!)
In case anyone wonders, I hit this without using JAVAP, just looking at the class files myself, I am only using javap to show it. (so it's unlikely to be a javap bug).
Consider:
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;
}.....
If you look at the output of javap -cs on the inner classes, they're surprisingly different!
public org.benf.cfr.tests.InnerClassTest1$Inner1(org.benf.cfr.tests.InnerClassTest1, java.util.List); Signature: (Lorg/benf/cfr/tests/InnerClassTest1;Ljava/util/List;)V
vs
public org.benf.cfr.tests.InnerClassTest2$Inner1(java.util.List<E>); Signature: (Lorg/benf/cfr/tests/InnerClassTest2;Ljava/util/List;)V
... the one which uses generics is missing the implicit parameter for the outer class! (it's correctly present in InnerClassTest1).
I can't find anything in the class file documentation to explain this - does anyone know why this might be?
Thanks!
Lee.
Update -
I've placed example files at http://www.benf.org/files/innerClassTest.tgz
Given Sean's answer below, I tried using javap on java 6, and I saw identical output for both, with no generic information - this leads me to believe that java 6's javap is not displaying full signature information?
The exact output I get using javap on 1.7.0_05-b06 is
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
}
Using the code above and using JDK 1.6.0_33 I get the following output:
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
}
and the only differences are that my implementation has (to make the code compile):
public int getX(int i) {
return i;
}
and the fact that your package name is probably different (org.benf.cfr.tests ?).
Other than that though, my output is pretty much the same. Are there any other differences in the code that might explain what you're seeing? From what I know about the compilation process and class files I wouldn't expect to see a difference in the output.
Good question - be interesting to find out why you're seeing this
Using Javap on InnerClassTest2$Inner1
gives
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);
}
Disassembling with Krakatau gives
.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
As you can see, the Krakatau output shows that the descriptor is actually correct, but for some reason Javap isn't displaying it. One thing about Javap is that it tries to arrange the output so it looks more like Java. Perhaps this is a new feature introduced in JDK7 that tries to make disassembled generics look more like Java by hiding the compiler added parameters. Unfortunately this makes Javap (even more) useless for seeing what's really there.
Interesting catch!
来源:https://stackoverflow.com/questions/15131040/java-inner-class-inconsistency-between-descriptor-and-signature-attribute-clas