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