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?
More information about records, including descriptions of the implicitly declared methods synthesized by the compiler, can be found in section 8.10 of The Java Language Specification . A record class is a shallowly immutable, transparent carrier for a fixed set of values, called the record components.
A RecordComponent provides information about, and dynamic access to, a component of a record class. See Java Language Specification: 8.10 Record Types.
A record class declares a sequence of fields, and then the appropriate accessors, constructors, equals , hashCode , and toString methods are created automatically. The fields are final because the class is intended to serve as a simple "data carrier".
If you want your record's constructor to do more than initialize its private fields, you can define a custom constructor for the record. However, unlike a class constructor, a record constructor doesn't have a formal parameter list; this is called a compact constructor.
Java record is a type of class whose sole purpose is to drive programming with immutable data. Let’s look at a simple example. So here we have created a record with header x and y. The x and y here are referred to as components of a record. Now, when we create a record, we get the following: Final fields based on the record components.
It is a marker interface that tells a tool that an annotation is to be documented. Annotations are not included in ‘Javadoc’ comments. The use of @Documented annotation in the code enables tools like Javadoc to process it and include the annotation type information in the generated document.
You simply cannot extend it with any other class, not even a record class. The only implicit superclass it has is java.lang.Record because defining record class explicitly by using extends only leads to compilation errors. Furthermore, the record classes are implicitly final. They cannot be declared abstract to allow further extensions.
The only implicit superclass it has is java.lang.Record. Defining this explicitly using extends will lead to compilation errors. Also, record classes are implicitly final. They cannot be declared abstract to allow further extensions. This means you cannot have any sub-records of a record.
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("\t\tof 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With