JDK-8257520 : Class::isRecord should check that the current class is final and not abstract
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.lang
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 16
  • Submitted: 2020-12-01
  • Updated: 2021-06-12
  • Resolved: 2020-12-04
Related Reports
CSR :  
Description
Summary
-------

Update Class::isRecord to only return true for classes that are final.

Problem
-------

The removal of non-specified JVM checks on classes with a Record
Attribute (see JDK-8255342), has resulted in more types of loadable
classes that may contain a Record Attribute. Since these checks are
not performed by the JVM anymore, `Class::isRecord`, and by extension
`Class::getRecordComponents`, may return true or component values,
respectively, for classes that are not well-formed record classes (as
per the JLS), .e.g. non-final or abstract classes, that contain a record
Attribute.

Solution
--------

Core Reflection, `Class::isRecord`, already asserts checks that the JVM
does not, e.g. that the direct superclass is `java.lang.Record`.

Some points from the Java Language Specification for record classes:

  1. It is a compile-time error if a record declaration has the modifier
     abstract.
  2. A record declaration is implicitly final.
  3. The direct superclass type of a record class is Record.

`Class::isRecord` already ensures no.3. This issue proposes to add
explicit checks in Core Reflection to ensure #1 and #2, since the JVM
now allows such classes that contain a Record Attribute to be loaded.

Note: asserting that a class is final is sufficient, as it implies that
the class it non-abstract - an assertion guaranteed by the JVM.

Specification
-------------

java.lang.Class:

       /**
        * Returns {@code true} if and only if this class is a record class.
        *
        * <p> The {@linkplain #getSuperclass() direct superclass} of a record
    -   * class is {@code java.lang.Record}. A record class has (possibly zero)
    -   * record components, that is, {@link #getRecordComponents()} returns a
    -   * non-null value.
    +   * class is {@code java.lang.Record}. A record class is {@linkplain
    +   * Modifier#FINAL final}. A record class has (possibly zero) record
    +   * components; {@link #getRecordComponents()} returns a non-null but
    +   * possibly empty value for a record.
        *
        * <p> Note that class {@link Record} is not a record type and thus invoking
        * this method on class {@code Record} returns {@code false}.
        *
        * @return true if and only if this class is a record class, otherwise false
        * @jls 8.10 Record Types
        * @since 16
        */
       public boolean isRecord() { ... }

Comments
Moving revised wording to Approved.
04-12-2020