Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8292159

TYPE_USE annotations on generic type arguments of record components discarded

XMLWordPrintable

    • b22
    • generic
    • generic

        ADDITIONAL SYSTEM INFORMATION :
        Distributor ID: Debian
        Description: Debian GNU/Linux 11 (bullseye)
        Release: 11
        Codename: bullseye
        ----
        Linux db 5.10.0-16-amd64 #1 SMP Debian 5.10.127-2 (2022-07-23) x86_64 GNU/Linux


        A DESCRIPTION OF THE PROBLEM :
        TYPE_USE annotations placed on generic type arguments of record components (e.g. List<@Annot String>) are for some reason discarded unless the record component type itself is annotated (e.g. @Annot List<@Annot String>).

        I compiled the example program in the "Steps to Reproduce'' section with javac 17, 18, and 20ea and executed all three versions. They all printed the same result: an empty annotations array for the String type use. However, annotating the List type, too, and running the programs again now showed the result I expected: String being annotated with @Annot.

        I also decompiled the class files via IntelliJ's decompiler: Interestingly, when the List was not annotated, the decompiled definition looked like:

        public static record R(List<String> strings) {
            public R(List<@Test.Annot String> strings) { this.strings = strings; }
            public List<@Test.Annot String> strings() { return this.strings; }
        }

        while the annotated version contained the annotation at both places in the component definition:

        public static record R(@Test.Annot List<@Test.Annot String> strings) {
            public R(@Test.Annot List<@Test.Annot String> strings) {this.strings = strings;}
            public @Test.Annot List<@Test.Annot String> strings() {return this.strings;}
        }


        I'm not sure whether this is actually a bug or simply not covered by the spec and, therefore, more of a feature request.


        STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
        Write the following code and execute its main method:

        package tmp;

        import java.lang.annotation.ElementType;
        import java.lang.annotation.Retention;
        import java.lang.annotation.RetentionPolicy;
        import java.lang.annotation.Target;
        import java.lang.reflect.AnnotatedParameterizedType;
        import java.lang.reflect.AnnotatedType;
        import java.lang.reflect.Field;
        import java.lang.reflect.RecordComponent;
        import java.util.Arrays;
        import java.util.List;

        public final class Test {
            @Target({ElementType.TYPE_USE})
            @Retention(RetentionPolicy.RUNTIME)
            public @interface Annot {}

            public static final class C {
                List<@Annot String> strings;
            }

            public record R(List<@Annot String> strings) {}

            public static void main(String[] args) {
                {
                    Field field = C.class.getDeclaredFields()[0];
                    AnnotatedParameterizedType type = (AnnotatedParameterizedType) field.getAnnotatedType();
                    AnnotatedType argument = type.getAnnotatedActualTypeArguments()[0];
                    System.out.println(Arrays.toString(argument.getAnnotations()));
                }
                {
                    RecordComponent component = R.class.getRecordComponents()[0];
                    AnnotatedParameterizedType type = (AnnotatedParameterizedType) component.getAnnotatedType();
                    AnnotatedType argument = type.getAnnotatedActualTypeArguments()[0];
                    System.out.println(Arrays.toString(argument.getAnnotations()));
                }
            }
        }


        EXPECTED VERSUS ACTUAL BEHAVIOR :
        EXPECTED -
        [@tmp.Test$Annot()]
        [@tmp.Test$Annot()]

        ACTUAL -
        [@tmp.Test$Annot()]
        []


        CUSTOMER SUBMITTED WORKAROUND :
        Instead of trying to access the annotation via the record component, you can access it via the field by using the same code used for class fields:

        Field field = R.class.getDeclaredFields()[0];
        ...etc.

        FREQUENCY : always


              sadayapalam Srikanth Adayapalam (Inactive)
              webbuggrp Webbug Group
              Votes:
              0 Vote for this issue
              Watchers:
              8 Start watching this issue

                Created:
                Updated:
                Resolved: