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.