Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 884
Dmitriy Dumanskiy Avatar asked Oct 13 '21 17:10

Dmitriy Dumanskiy


People also ask

What Are records in Java 17?

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.

What is record component Java?

A RecordComponent provides information about, and dynamic access to, a component of a record class. See Java Language Specification: 8.10 Record Types.

What is the use of record class in Java?

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".

Can records have constructors?

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.

What is a record in Java?

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.

What is @documented annotation in Java?

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.

How to extend record class in Java?

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.

What is the implicit superclass of record class in Java?

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.


1 Answers

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.

like image 112
Holger Avatar answered Oct 26 '22 11:10

Holger