Inverse annotation inheritance issue with generic interfaces
Lukas Magel
luke-mail at online.de
Mon Nov 23 22:43:00 UTC 2015
In the listing in my previous e-mail I forgot to mention that the
OpenJDK was used for all constellations. Here's the javap output for the
Main class compiled with OpenJDK version 1.7.0_51 vs 1.7.0_85. I hope
this is correct. I only compiled the test project once using the Oracle
JDK 7u79 to cross-check but was unable to reproduce the issue.
1.7.0_51
Classfile Main.class
Last modified Nov 23, 2015; size 1155 bytes
MD5 checksum 24e9fdb3ed8e97132f012579dadbbf90
Compiled from "Main.java"
public class Main extends java.lang.Object implements SampleInterface<B>
Signature: #29 //
Ljava/lang/Object;LSampleInterface<LB;>;
SourceFile: "Main.java"
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #9.#32 // java/lang/Object."<init>":()V
#2 = Class #33 // Main
#3 = Fieldref #34.#35 //
java/lang/System.out:Ljava/io/PrintStream;
#4 = Methodref #36.#37 //
java/io/PrintStream.println:(Ljava/lang/Object;)V
#5 = Methodref #38.#39 //
java/lang/Class.getMethods:()[Ljava/lang/reflect/Method;
#6 = Methodref #40.#41 //
java/lang/reflect/Method.getAnnotations:()[Ljava/lang/annotation/Annotation;
#7 = Class #42 // B
#8 = Methodref #2.#43 // Main.sampleMethod:(LB;)V
#9 = Class #44 // java/lang/Object
#10 = Class #45 // SampleInterface
#11 = Utf8 <init>
#12 = Utf8 ()V
#13 = Utf8 Code
#14 = Utf8 LineNumberTable
#15 = Utf8 main
#16 = Utf8 ([Ljava/lang/String;)V
#17 = Utf8 StackMapTable
#18 = Class #46 // "[Ljava/lang/String;"
#19 = Class #47 // java/lang/Class
#20 = Class #48 // "[Ljava/lang/reflect/Method;"
#21 = Class #49 // java/lang/reflect/Method
#22 = Class #50 //
"[Ljava/lang/annotation/Annotation;"
#23 = Utf8 sampleMethod
#24 = Utf8 (LB;)V
#25 = Utf8 RuntimeVisibleAnnotations
#26 = Utf8 LSampleAnnotation;
#27 = Utf8 (LA;)V
#28 = Utf8 Signature
#29 = Utf8 Ljava/lang/Object;LSampleInterface<LB;>;
#30 = Utf8 SourceFile
#31 = Utf8 Main.java
#32 = NameAndType #11:#12 // "<init>":()V
#33 = Utf8 Main
#34 = Class #51 // java/lang/System
#35 = NameAndType #52:#53 // out:Ljava/io/PrintStream;
#36 = Class #54 // java/io/PrintStream
#37 = NameAndType #55:#56 // println:(Ljava/lang/Object;)V
#38 = Class #47 // java/lang/Class
#39 = NameAndType #57:#58 //
getMethods:()[Ljava/lang/reflect/Method;
#40 = Class #49 // java/lang/reflect/Method
#41 = NameAndType #59:#60 //
getAnnotations:()[Ljava/lang/annotation/Annotation;
#42 = Utf8 B
#43 = NameAndType #23:#24 // sampleMethod:(LB;)V
#44 = Utf8 java/lang/Object
#45 = Utf8 SampleInterface
#46 = Utf8 [Ljava/lang/String;
#47 = Utf8 java/lang/Class
#48 = Utf8 [Ljava/lang/reflect/Method;
#49 = Utf8 java/lang/reflect/Method
#50 = Utf8 [Ljava/lang/annotation/Annotation;
#51 = Utf8 java/lang/System
#52 = Utf8 out
#53 = Utf8 Ljava/io/PrintStream;
#54 = Utf8 java/io/PrintStream
#55 = Utf8 println
#56 = Utf8 (Ljava/lang/Object;)V
#57 = Utf8 getMethods
#58 = Utf8 ()[Ljava/lang/reflect/Method;
#59 = Utf8 getAnnotations
#60 = Utf8 ()[Ljava/lang/annotation/Annotation;
{
public Main();
Signature: ()V
flags: ACC_PUBLIC
LineNumberTable:
line 6: 0
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method
java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 6: 0
public static void main(java.lang.String[]);
Signature: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
LineNumberTable:
line 9: 0
line 10: 4
line 11: 11
line 12: 34
line 13: 42
line 14: 71
line 13: 79
line 11: 85
line 17: 91
Code:
stack=2, locals=10, args_size=1
0: ldc_w #2 // class Main
3: astore_1
4: getstatic #3 // Field
java/lang/System.out:Ljava/io/PrintStream;
7: aload_1
8: invokevirtual #4 // Method
java/io/PrintStream.println:(Ljava/lang/Object;)V
11: aload_1
12: invokevirtual #5 // Method
java/lang/Class.getMethods:()[Ljava/lang/reflect/Method;
15: astore_2
16: aload_2
17: arraylength
18: istore_3
19: iconst_0
20: istore 4
22: iload 4
24: iload_3
25: if_icmpge 91
28: aload_2
29: iload 4
31: aaload
32: astore 5
34: getstatic #3 // Field
java/lang/System.out:Ljava/io/PrintStream;
37: aload 5
39: invokevirtual #4 // Method
java/io/PrintStream.println:(Ljava/lang/Object;)V
42: aload 5
44: invokevirtual #6 // Method
java/lang/reflect/Method.getAnnotations:()[Ljava/lang/annotation/Annotation;
47: astore 6
49: aload 6
51: arraylength
52: istore 7
54: iconst_0
55: istore 8
57: iload 8
59: iload 7
61: if_icmpge 85
64: aload 6
66: iload 8
68: aaload
69: astore 9
71: getstatic #3 // Field
java/lang/System.out:Ljava/io/PrintStream;
74: aload 9
76: invokevirtual #4 // Method
java/io/PrintStream.println:(Ljava/lang/Object;)V
79: iinc 8, 1
82: goto 57
85: iinc 4, 1
88: goto 22
91: return
LineNumberTable:
line 9: 0
line 10: 4
line 11: 11
line 12: 34
line 13: 42
line 14: 71
line 13: 79
line 11: 85
line 17: 91
StackMapTable: number_of_entries = 4
frame_type = 255 /* full_frame */
offset_delta = 22
locals = [ class "[Ljava/lang/String;", class
java/lang/Class, class "[Ljava/lang/reflect/Method;", int, int ]
stack = []
frame_type = 255 /* full_frame */
offset_delta = 34
locals = [ class "[Ljava/lang/String;", class
java/lang/Class, class "[Ljava/lang/reflect/Method;", int, int, class
java/lang/reflect/Method, class "[Ljava/lang/annotation/Annotation;",
int, int ]
stack = []
frame_type = 255 /* full_frame */
offset_delta = 27
locals = [ class "[Ljava/lang/String;", class
java/lang/Class, class "[Ljava/lang/reflect/Method;", int, int ]
stack = []
frame_type = 248 /* chop */
offset_delta = 5
public void sampleMethod(B);
Signature: (LB;)V
flags: ACC_PUBLIC
LineNumberTable:
line 23: 0
Code:
stack=0, locals=2, args_size=2
0: return
LineNumberTable:
line 23: 0
RuntimeVisibleAnnotations:
0: #26()
public void sampleMethod(A);
Signature: (LA;)V
flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
LineNumberTable:
line 6: 0
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: checkcast #7 // class B
5: invokevirtual #8 // Method sampleMethod:(LB;)V
8: return
LineNumberTable:
line 6: 0
}
1.7.0_85
Classfile Main.class
Last modified Nov 23, 2015; size 1167 bytes
MD5 checksum a4f2d871550c785d2a87a21bd34bd1fb
Compiled from "Main.java"
public class Main extends java.lang.Object implements SampleInterface<B>
Signature: #29 //
Ljava/lang/Object;LSampleInterface<LB;>;
SourceFile: "Main.java"
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #9.#32 // java/lang/Object."<init>":()V
#2 = Class #33 // Main
#3 = Fieldref #34.#35 //
java/lang/System.out:Ljava/io/PrintStream;
#4 = Methodref #36.#37 //
java/io/PrintStream.println:(Ljava/lang/Object;)V
#5 = Methodref #38.#39 //
java/lang/Class.getMethods:()[Ljava/lang/reflect/Method;
#6 = Methodref #40.#41 //
java/lang/reflect/Method.getAnnotations:()[Ljava/lang/annotation/Annotation;
#7 = Class #42 // B
#8 = Methodref #2.#43 // Main.sampleMethod:(LB;)V
#9 = Class #44 // java/lang/Object
#10 = Class #45 // SampleInterface
#11 = Utf8 <init>
#12 = Utf8 ()V
#13 = Utf8 Code
#14 = Utf8 LineNumberTable
#15 = Utf8 main
#16 = Utf8 ([Ljava/lang/String;)V
#17 = Utf8 StackMapTable
#18 = Class #46 // "[Ljava/lang/String;"
#19 = Class #47 // java/lang/Class
#20 = Class #48 // "[Ljava/lang/reflect/Method;"
#21 = Class #49 // java/lang/reflect/Method
#22 = Class #50 //
"[Ljava/lang/annotation/Annotation;"
#23 = Utf8 sampleMethod
#24 = Utf8 (LB;)V
#25 = Utf8 RuntimeVisibleAnnotations
#26 = Utf8 LSampleAnnotation;
#27 = Utf8 (LA;)V
#28 = Utf8 Signature
#29 = Utf8 Ljava/lang/Object;LSampleInterface<LB;>;
#30 = Utf8 SourceFile
#31 = Utf8 Main.java
#32 = NameAndType #11:#12 // "<init>":()V
#33 = Utf8 Main
#34 = Class #51 // java/lang/System
#35 = NameAndType #52:#53 // out:Ljava/io/PrintStream;
#36 = Class #54 // java/io/PrintStream
#37 = NameAndType #55:#56 // println:(Ljava/lang/Object;)V
#38 = Class #47 // java/lang/Class
#39 = NameAndType #57:#58 //
getMethods:()[Ljava/lang/reflect/Method;
#40 = Class #49 // java/lang/reflect/Method
#41 = NameAndType #59:#60 //
getAnnotations:()[Ljava/lang/annotation/Annotation;
#42 = Utf8 B
#43 = NameAndType #23:#24 // sampleMethod:(LB;)V
#44 = Utf8 java/lang/Object
#45 = Utf8 SampleInterface
#46 = Utf8 [Ljava/lang/String;
#47 = Utf8 java/lang/Class
#48 = Utf8 [Ljava/lang/reflect/Method;
#49 = Utf8 java/lang/reflect/Method
#50 = Utf8 [Ljava/lang/annotation/Annotation;
#51 = Utf8 java/lang/System
#52 = Utf8 out
#53 = Utf8 Ljava/io/PrintStream;
#54 = Utf8 java/io/PrintStream
#55 = Utf8 println
#56 = Utf8 (Ljava/lang/Object;)V
#57 = Utf8 getMethods
#58 = Utf8 ()[Ljava/lang/reflect/Method;
#59 = Utf8 getAnnotations
#60 = Utf8 ()[Ljava/lang/annotation/Annotation;
{
public Main();
Signature: ()V
flags: ACC_PUBLIC
LineNumberTable:
line 6: 0
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method
java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 6: 0
public static void main(java.lang.String[]);
Signature: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
LineNumberTable:
line 9: 0
line 10: 4
line 11: 11
line 12: 34
line 13: 42
line 14: 71
line 13: 79
line 11: 85
line 17: 91
Code:
stack=2, locals=10, args_size=1
0: ldc_w #2 // class Main
3: astore_1
4: getstatic #3 // Field
java/lang/System.out:Ljava/io/PrintStream;
7: aload_1
8: invokevirtual #4 // Method
java/io/PrintStream.println:(Ljava/lang/Object;)V
11: aload_1
12: invokevirtual #5 // Method
java/lang/Class.getMethods:()[Ljava/lang/reflect/Method;
15: astore_2
16: aload_2
17: arraylength
18: istore_3
19: iconst_0
20: istore 4
22: iload 4
24: iload_3
25: if_icmpge 91
28: aload_2
29: iload 4
31: aaload
32: astore 5
34: getstatic #3 // Field
java/lang/System.out:Ljava/io/PrintStream;
37: aload 5
39: invokevirtual #4 // Method
java/io/PrintStream.println:(Ljava/lang/Object;)V
42: aload 5
44: invokevirtual #6 // Method
java/lang/reflect/Method.getAnnotations:()[Ljava/lang/annotation/Annotation;
47: astore 6
49: aload 6
51: arraylength
52: istore 7
54: iconst_0
55: istore 8
57: iload 8
59: iload 7
61: if_icmpge 85
64: aload 6
66: iload 8
68: aaload
69: astore 9
71: getstatic #3 // Field
java/lang/System.out:Ljava/io/PrintStream;
74: aload 9
76: invokevirtual #4 // Method
java/io/PrintStream.println:(Ljava/lang/Object;)V
79: iinc 8, 1
82: goto 57
85: iinc 4, 1
88: goto 22
91: return
LineNumberTable:
line 9: 0
line 10: 4
line 11: 11
line 12: 34
line 13: 42
line 14: 71
line 13: 79
line 11: 85
line 17: 91
StackMapTable: number_of_entries = 4
frame_type = 255 /* full_frame */
offset_delta = 22
locals = [ class "[Ljava/lang/String;", class
java/lang/Class, class "[Ljava/lang/reflect/Method;", int, int ]
stack = []
frame_type = 255 /* full_frame */
offset_delta = 34
locals = [ class "[Ljava/lang/String;", class
java/lang/Class, class "[Ljava/lang/reflect/Method;", int, int, class
java/lang/reflect/Method, class "[Ljava/lang/annotation/Annotation;",
int, int ]
stack = []
frame_type = 255 /* full_frame */
offset_delta = 27
locals = [ class "[Ljava/lang/String;", class
java/lang/Class, class "[Ljava/lang/reflect/Method;", int, int ]
stack = []
frame_type = 248 /* chop */
offset_delta = 5
public void sampleMethod(B);
Signature: (LB;)V
flags: ACC_PUBLIC
LineNumberTable:
line 23: 0
Code:
stack=0, locals=2, args_size=2
0: return
LineNumberTable:
line 23: 0
RuntimeVisibleAnnotations:
0: #26()
public void sampleMethod(A);
Signature: (LA;)V
flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
LineNumberTable:
line 6: 0
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: checkcast #7 // class B
5: invokevirtual #8 // Method sampleMethod:(LB;)V
8: return
LineNumberTable:
line 6: 0
RuntimeVisibleAnnotations:
0: #26()
}
On 23.11.2015 21:41, Alex Buckley wrote:
> Enumerating the methods of Main.class shouldn't reveal
> sampleMethod(A), since sampleMethod(B) overrides it. I suspect a
> bridge method bug is being tickled, probably in Core Reflection rather
> than javac.
>
> What is the full javap output for Main.class as compiled with Oracle
> JDK 1.7.0_51 versus Oracle JDK 1.7.0_85 ? (Plain text inline, please.)
>
> Alex
>
> On 11/23/2015 5:35 AM, Lukas Magel wrote:
>> Hello,
>>
>> I am currently working on a Java EE web project and have been
>> experiencing a compiler behavior which I don't know how to interpret.
>>
>> In our code we define interfaces like the following:
>>
>> public interface SampleInterface<T extends A> {
>>
>> public void sampleMethod(T t);
>>
>> }
>>
>> as well as an implementing class:
>>
>> public class Main implements SampleInterface<B> {
>>
>> @SampleAnnotation
>> public void sampleMethod(B t) {
>>
>> }
>> }
>>
>> and the corresponding annotation:
>>
>> @Retention(RetentionPolicy.RUNTIME)
>> @Target(ElementType.METHOD)
>> public @interface SampleAnnotation {
>>
>> }
>>
>> Where Class B extends A. All classes are compiled using the OpenJDK
>> compiler. If I use Reflection to retrieve all methods of the Main class
>> at runtime and print each method with its annotations I get the
>> following list of methods:
>>
>> class Main
>>
>> public void Main.sampleMethod(A)
>> @SampleAnnotation()
>>
>> public void Main.sampleMethod(B)
>> @SampleAnnotation()
>>
>> The compiler will assign the annotation to the method declaration of the
>> interface although the annotation is only declared in the implementing
>> class. I cannot reproduce this behavior with the Oracle Java Compiler or
>> the Eclipse JDT Compiler. If I compile the example with one of the two
>> compilers the resulting program will yield the following result:
>>
>> class annotation.Main
>>
>> public void Main.sampleMethod(A)
>>
>> public void Main.sampleMethod(B)
>> @SampleAnnotation()
>>
>> The JRE type (Oracle, OpenJDK) that the program is executed with has no
>> influence on the result.
>>
>> This behavior is reproducible with the following constellations:
>> JDK Operating System Hardware
>> 1.7.0_85 Ubuntu 14.04.03 x86-64
>> 1.7.0_91 Arch Linux x86-64
>> 1.8.0_66 Arch Linux x86-64
>>
>> It is NOT reproducible with the following constellation:
>> 1.7.0_51 Ubuntu 13.04 x86-64
>>
>> My actual question is whether this behavior is intended or not. It
>> causes quite some issues with our Jax-RS REST implementations. All REST
>> classes each implement an interface like the one above and are annotated
>> with multiple annotations to allow the container to identify endpoints
>> at runtime. The Jax-RS framework uses reflection to determine possible
>> method candidates at runtime and will happily accept both the generic
>> and the special method since both of them carry the annotations.
>>
>> I can also provide an example project if necessary.
>>
>> Thanks,
>> Lukas
More information about the compiler-dev
mailing list