Skip to content
Advertisement

Why RecordComponent doesn’t have annotation info that is defined in the Records class in Java 17?

I play with records and found something that doesn’t look logical to me:

Record:

record R(@NotEmpty Integer i) {}

Code:

RecordComponent[] recordComponents = R.class.getRecordComponents();
System.out.println(recordComponents[0].isAnnotationPresent(NotEmpty.class));
//prints false

However, if I do:

System.out.println(R.class.getDeclaredFields()[0].isAnnotationPresent(NotEmpty.class));
//prints true

Is that expected? Because RecordComponent implements AnnotatedElement, so I thought RecordComponent should have info about annotations. Are my expectations wrong?

Advertisement

Answer

This depends on the specified permitted targets of the annotation. The record component has an associated constructor parameter, field, accessor method, and a type. The compiler will accept an annotation if at least one of these locations is permitted, but the annotation will only get associated with the permitted locations of the record.

So the following code

public class RecordAnnotations {
    public static void main(String[] args) {
        Class<?> cl = Test.class;
        for(var r: cl.getRecordComponents()) {
            System.out.println("record component "
                + Arrays.toString(r.getAnnotations()) + " " + r);
            System.out.println("tof type " + r.getAnnotatedType());
            System.out.println("taccessed by " +
                Arrays.toString(r.getAccessor().getAnnotations())+" "+r.getAccessor());
            System.out.println("twith return type "
                + r.getAccessor().getAnnotatedReturnType());
        }
        System.out.println();
        for(var r: cl.getDeclaredFields()) {
            System.out.println("field " + Arrays.toString(r.getAnnotations())+" "+r);
            System.out.println("tof type " + r.getAnnotatedType());
        }
        System.out.println();

        for(var c: cl.getDeclaredConstructors()) {
            System.out.println("constructor " + c);
            for(var r: c.getParameters()) {
                System.out.println("tparameter "
                    + Arrays.toString(r.getAnnotations()) + " " + r);
                System.out.println("ttof type " + r.getAnnotatedType());
            }
        }
        //System.out.println();
        //for(var r: cl.getRecordComponents()) System.out.println(r);
    }
}

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.RECORD_COMPONENT)
@interface WithRecord {}

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD)
@interface WithField {}

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER)
@interface WithParameter {}

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE_USE)
@interface WithTypeUse {}

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
@interface WithMethod {}

record Test(@WithRecord @WithField @WithParameter @WithTypeUse @WithMethod
            int component) {
}

prints

record component [@WithRecord()] int component
        of type @WithTypeUse() int
        accessed by [@WithMethod()] public int Test.component()
        with return type @WithTypeUse() int

field [@WithField()] private final int Test.component
        of type @WithTypeUse() int

constructor Test(int)
        parameter [@WithParameter()] int component
                of type @WithTypeUse() int

showing how each annotation has been copied to its permitted locations. Only the annotations having RECORD_COMPONENT as permitted target, are retrievable through RecordComponent’s getAnnotation or isAnnotationPresent.

Of course, @Retention(RetentionPolicy.RUNTIME) is also required.

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement