JDK-8233436 : Preview APIs support for records
  • Type: CSR
  • Component: core-libs
  • Sub-Component: java.lang
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 14
  • Submitted: 2019-11-03
  • Updated: 2020-07-01
  • Resolved: 2019-11-26
Related Reports
CSR :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
Summary
-------

Add support for record classes, see ([JEP 359](http://openjdk.java.net/jeps/359)), in core-libs.

Problem
-------

Records classes, see ([JEP 359](http://openjdk.java.net/jeps/359)), will be previewed in Java SE 14 and some support will be needed for them in core-libs. Records will need reflection support, for records and its record components. Also records components will be a new annotation target, this target needs to be added to the corresponding API. Also a common supertype that defines the API for records will be needed.

In addition given that the implementation of methods: <code>toString</code>, <code>equals</code> and <code>hashCode</code> follows a very similar pattern for all records, it is sound to provide a common implementation for those methods.

Solution
--------

Records allow defining shallowly immutable, transparent carriers for a fixed set of values, the record components. Record components are first class entities in the design of records as they determine the API of a record. For this reason apart from enhancing the API of <code>java.lang.Class</code> to support records, a new class, <code>java.lang.reflect.RecordComponent</code>, has been defined to allow reflecting record components. In addition record components are a new annotation target, this has to be represented in class <code>java.lang.annotation.ElementType</code>

A common supertype for all records has been defined in class <code>java.lang.Record</code>. This class will defined the common API for records. In addition, a new class has been defined,<code>java.lang.runtime.ObjectMethods</code>, which will provide a common bootstrap method for the implementation of methods: <code>toString</code>, <code>equals</code> and <code>hashCode</code>. These feature will constitute a preview feature ([JEP 12](http://openjdk.java.net/jeps/12)) in Java SE 14.

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

In addition to the API changes below, the serialization specification is also updated; see attached file for those changes.

    diff -r 5573a7098439 src/java.base/share/classes/java/lang/Class.java
    --- a/src/java.base/share/classes/java/lang/Class.java	Sat Nov 02 10:02:18 2019 +0000
    +++ b/src/java.base/share/classes/java/lang/Class.java	Mon Nov 25 16:16:40 2019 -0500
    @@ -2267,6 +2268,68 @@
             return copyFields(privateGetDeclaredFields(false));
         }
     
    +    /**
    +     * {@preview Associated with records, a preview feature of the Java language.
    +     *
    +     *           This method is associated with <i>records</i>, a preview
    +     *           feature of the Java language. Preview features
    +     *           may be removed in a future release, or upgraded to permanent
    +     *           features of the Java language.}
    +     *
    +     * Returns an array containing {@code RecordComponent} objects reflecting all the
    +     * declared record components of the record represented by this {@code Class} object.
    +     * The components are returned in the same order that they are declared in the
    +     * record header.
    +     *
    +     * @return  The array of {@code RecordComponent} objects representing all the
    +     *          record components of this record. The array is empty if this class
    +     *          is not a record, or if this class is a record with no components.
    +     * @throws  SecurityException
    +     *          If a security manager, <i>s</i>, is present and any of the
    +     *          following conditions is met:
    +     *
    +     *          <ul>
    +     *
    +     *          <li> the caller's class loader is not the same as the
    +     *          class loader of this class and invocation of
    +     *          {@link SecurityManager#checkPermission
    +     *          s.checkPermission} method with
    +     *          {@code RuntimePermission("accessDeclaredMembers")}
    +     *          denies access to the declared methods within this class
    +     *
    +     *          <li> the caller's class loader is not the same as or an
    +     *          ancestor of the class loader for the current class and
    +     *          invocation of {@link SecurityManager#checkPackageAccess
    +     *          s.checkPackageAccess()} denies access to the package
    +     *          of this class
    +     *
    +     *          </ul>
    +     *
    +     * @jls 8.10 Record Types
    +     * @since 14
    +     */
    +    @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
    +                                 essentialAPI=false)
    +    @SuppressWarnings("preview")
    +    @CallerSensitive
    +    public RecordComponent[] getRecordComponents() {
    +    }
     
         /**
          * Returns an array containing {@code Method} objects reflecting all the
    @@ -3531,6 +3596,29 @@
             this.getSuperclass() == java.lang.Enum.class;
         }
     
    +    /**
    +     * {@preview Associated with records, a preview feature of the Java language.
    +     *
    +     *           This method is associated with <i>records</i>, a preview
    +     *           feature of the Java language. Preview features
    +     *           may be removed in a future release, or upgraded to permanent
    +     *           features of the Java language.}
    +     *
    +     * Returns {@code true} if and only if this class is a record class.
    +     * It returns {@code false} otherwise. Note that class {@link Record} is not a
    +     * record type and thus invoking this method on class {@link java.lang.Record}
    +     * returns {@code false}.
    +     *
    +     * @return true if and only if this class is a record class
    +     * @jls 8.10 Record Types
    +     * @since 14
    +     */
    +    @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
    +                                 essentialAPI=false)
    +    public boolean isRecord() {
    +    }
    +
         // Fetches the factory for reflective objects
         private static ReflectionFactory getReflectionFactory() {
             if (reflectionFactory == null) {
    diff -r 5573a7098439 src/java.base/share/classes/java/lang/Record.java
    --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    +++ b/src/java.base/share/classes/java/lang/Record.java	Mon Nov 25 16:16:40 2019 -0500
    @@ -0,0 +1,154 @@
    +package java.lang;
    +
    +/**
    + * {@preview Associated with records, a preview feature of the Java language.
    + *
    + *           This class is associated with <i>records</i>, a preview
    + *           feature of the Java language. Programs can only use this
    + *           class when preview features are enabled. Preview features
    + *           may be removed in a future release, or upgraded to permanent
    + *           features of the Java language.}
    + *
    + * This is the common base class of all Java language record classes.
    + *
    + * <p>More information about records, including descriptions of the
    + * implicitly declared methods synthesized by the compiler, can be
    + * found in section 8.10 of
    + * <cite>The Java&trade; Language Specification</cite>.
    + *
    + * <p>A <em>record class</em> is a shallowly immutable, transparent carrier for
    + * a fixed set of values, called the <em>record components</em>.  The Java&trade;
    + * language provides concise syntax for declaring record classes, whereby the
    + * record components are declared in the record header.  The list of record
    + * components declared in the record header form the <em>record descriptor</em>.
    + *
    + * <p>A record class has the following mandated members: a public <em>canonical
    + * constructor</em>, whose descriptor is the same as the record descriptor;
    + * a private final field corresponding to each component, whose name and
    + * type are the same as that of the component; a public accessor method
    + * corresponding to each component, whose name and return type are the same as
    + * that of the component.  If not explicitly declared in the body of the record,
    + * implicit implementations for these members are provided.
    + *
    + * <p>The implicit declaration of the canonical constructor initializes the
    + * component fields from the corresponding constructor arguments.  The implicit
    + * declaration of the accessor methods returns the value of the corresponding
    + * component field.  The implicit declaration of the {@link Object#equals(Object)},
    + * {@link Object#hashCode()}, and {@link Object#toString()} methods are derived
    + * from all of the component fields.
    + *
    + * <p>The primary reasons to provide an explicit declaration for the
    + * canonical constructor or accessor methods are to validate constructor
    + * arguments, perform defensive copies on mutable components, or normalize groups
    + * of components (such as reducing a rational number to lowest terms.)
    + *
    + * <p>For all record classes, the following invariant must hold: if a record R's
    + * components are {@code c1, c2, ... cn}, then if a record instance is copied
    + * as follows:
    + * <pre>
    + *     R copy = new R(r.c1(), r.c2(), ..., r.cn());
    + * </pre>
    + * then it must be the case that {@code r.equals(copy)}.
    + *
    + * @apiNote
    + * A record class that {@code implements} {@link java.io.Serializable} is said
    + * to be a <i>serializable record</i>. Serializable records are serialized and
    + * deserialized differently than ordinary serializable objects. During
    + * deserialization the record's canonical constructor is invoked to construct
    + * the record object. Certain serialization-related methods, such as readObject
    + * and writeObject, are ignored for serializable records. More information about
    + * serializable records can be found in
    + * <a href="{@docRoot}/java.base/java/io/ObjectInputStream.html#record-serialization">record serialization</a>.
    + *
    + * @jls 8.10 Record Types
    + * @since 14
    + */
    +@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
    +                             essentialAPI=true)
    +public abstract class Record {
    +    /**
    +     * Indicates whether some other object is "equal to" this one.  In addition
    +     * to the general contract of {@link Object#equals(Object)},
    +     * record classes must further participate in the invariant that when
    +     * a record instance is "copied" by passing the result of the record component
    +     * accessor methods to the canonical constructor, as follows:
    +     * <pre>
    +     *     R copy = new R(r.c1(), r.c2(), ..., r.cn());
    +     * </pre>
    +     * then it must be the case that {@code r.equals(copy)}.
    +     *
    +     * @implSpec
    +     * The implicitly provided implementation returns {@code true} if and
    +     * only if the argument is an instance of the same record type as this object,
    +     * and each component of this record is equal to the corresponding component
    +     * of the argument, according to {@link java.util.Objects#equals(Object,Object)}
    +     * for components whose types are reference types, and according to the semantics
    +     * of the {@code equals} method on the corresponding primitive wrapper type.
    +     *
    +     * @see java.util.Objects#equals(Object,Object)
    +     *
    +     * @param   obj   the reference object with which to compare.
    +     * @return  {@code true} if this object is the same as the obj
    +     *          argument; {@code false} otherwise.
    +     */
    +    @Override
    +    public abstract boolean equals(Object obj);
    +
    +    /**
    +     * Obeys the general contract of {@link Object#hashCode Object.hashCode}.
    +     *
    +     * @implSpec
    +     * The implicitly provided implementation returns a hash code value derived
    +     * by combining the hash code value for all the components, according to
    +     * {@link Object#hashCode()} for components whose types are reference types,
    +     * or the primitive wrapper hash code for components whose types are primitive
    +     * types.
    +     *
    +     * @see     Object#hashCode()
    +     *
    +     * @return  a hash code value for this object.
    +     */
    +    @Override
    +    public abstract int hashCode();
    +
    +    /**
    +     * Obeys the general contract of {@link Object#toString Object.toString}.
    +     *
    +     * @implSpec
    +     * The implicitly provided implementation returns a string that is derived
    +     * from the name of the record class and the names and string representations
    +     * of all the components, according to {@link Object#toString()} for components
    +     * whose types are reference types, and the primitive wrapper {@code toString}
    +     * method for components whose types are primitive types.
    +     *
    +     * @see     Object#toString()
    +     *
    +     * @return  a string representation of the object.
    +     */
    +    @Override
    +    public abstract String toString();
    +}
    diff -r 5573a7098439 src/java.base/share/classes/java/lang/annotation/ElementType.java
    --- a/src/java.base/share/classes/java/lang/annotation/ElementType.java	Sat Nov 02 10:02:18 2019 +0000
    +++ b/src/java.base/share/classes/java/lang/annotation/ElementType.java	Mon Nov 25 16:16:40 2019 -0500
    @@ -114,5 +114,25 @@
          *
          * @since 9
          */
    -    MODULE
    +    MODULE,
    +
    +    /**
    +     * {@preview Associated with records, a preview feature of the Java language.
    +     *
    +     *           This constant is associated with <i>records</i>, a preview
    +     *           feature of the Java language. Programs can only use this
    +     *           constant when preview features are enabled. Preview features
    +     *           may be removed in a future release, or upgraded to permanent
    +     *           features of the Java language.}
    +     *
    +     * Record component
    +     *
    +     * @jls 8.10.3 Record Members
    +     * @jls 9.7.4 Where Annotations May Appear
    +     *
    +     * @since 14
    +     */
    +    @jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
    +                                 essentialAPI=true)
    +    RECORD_COMPONENT;
     }
    diff -r 5573a7098439 src/java.base/share/classes/java/lang/reflect/RecordComponent.java
    --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    +++ b/src/java.base/share/classes/java/lang/reflect/RecordComponent.java	Mon Nov 25 16:16:40 2019 -0500
    @@ -0,0 +1,254 @@
    +package java.lang.reflect;
    +
    +import jdk.internal.access.SharedSecrets;
    +import sun.reflect.annotation.AnnotationParser;
    +import sun.reflect.annotation.TypeAnnotation;
    +import sun.reflect.annotation.TypeAnnotationParser;
    +import sun.reflect.generics.factory.CoreReflectionFactory;
    +import sun.reflect.generics.factory.GenericsFactory;
    +import sun.reflect.generics.repository.FieldRepository;
    +import sun.reflect.generics.scope.ClassScope;
    +import java.lang.annotation.Annotation;
    +import java.util.Map;
    +import java.util.Objects;
    +
    +/**
    + * {@preview Associated with records, a preview feature of the Java language.
    + *
    + *           This class is associated with <i>records</i>, a preview
    + *           feature of the Java language. Preview features
    + *           may be removed in a future release, or upgraded to permanent
    + *           features of the Java language.}
    + *
    + * A {@code RecordComponent} provides information about, and dynamic access to, a
    + * component of a record class.
    + *
    + * @see Class#getRecordComponents()
    + * @see java.lang.Record
    + * @jls 8.10 Record Types
    + * @since 14
    + */
    +@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
    +                             essentialAPI=false)
    +public final class RecordComponent implements AnnotatedElement {
    +    /**
    +     * Returns the name of this record component.
    +     *
    +     * @return the name of this record component
    +     */
    +    public String getName() {
    +    }
    +
    +    /**
    +     * Returns a {@code Class} that identifies the declared type for this
    +     * record component.
    +     *
    +     * @return a {@code Class} identifying the declared type of the component
    +     * represented by this record component
    +     */
    +    public Class<?> getType() {
    +    }
    +
    +    /**
    +     * Returns a {@code String} that describes the  generic type signature for
    +     * this record component.
    +     *
    +     * @return a {@code String} that describes the generic type signature for
    +     * this record component
    +     *
    +     * @jvms 4.7.9.1 Signatures
    +     */
    +    public String getGenericSignature() {
    +    }
    +
    +    /**
    +     * Returns a {@code Type} object that represents the declared type for
    +     * this record component.
    +     *
    +     * <p>If the declared type of the record component is a parameterized type,
    +     * the {@code Type} object returned reflects the actual type arguments used
    +     * in the source code.
    +     *
    +     * <p>If the type of the underlying record component is a type variable or a
    +     * parameterized type, it is created. Otherwise, it is resolved.
    +     *
    +     * @return a {@code Type} object that represents the declared type for
    +     *         this record component
    +     * @throws GenericSignatureFormatError if the generic record component
    +     *         signature does not conform to the format specified in
    +     *         <cite>The Java&trade; Virtual Machine Specification</cite>
    +     * @throws TypeNotPresentException if the generic type
    +     *         signature of the underlying record component refers to a non-existent
    +     *         type declaration
    +     * @throws MalformedParameterizedTypeException if the generic
    +     *         signature of the underlying record component refers to a parameterized
    +     *         type that cannot be instantiated for any reason
    +     */
    +    public Type getGenericType() {
    +    }
    +
    +    /**
    +     * Returns an {@code AnnotatedType} object that represents the use of a type to specify
    +     * the declared type of this record component.
    +     *
    +     * @return an object representing the declared type of this record component
    +     */
    +    public AnnotatedType getAnnotatedType() {
    +    }
    +
    +    /**
    +     * Returns a {@code Method} that represents the accessor for this record
    +     * component.
    +     *
    +     * @return a {@code Method} that represents the accessor for this record
    +     * component
    +     */
    +    public Method getAccessor() {
    +    }
    +
    +    /**
    +     * @throws NullPointerException {@inheritDoc}
    +     */
    +    @Override
    +    public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
    +    }
    +
    +    /**
    +     * {@inheritDoc}
    +     */
    +    @Override
    +    public Annotation[] getAnnotations() {
    +    }
    +
    +    /**
    +     * {@inheritDoc}
    +     */
    +    @Override
    +    public Annotation[] getDeclaredAnnotations() {}
    +
    +    /**
    +     * Returns a string describing this record component. The format is
    +     * the record component type, followed by a space, followed by the name
    +     * of the record component.
    +     * For example:
    +     * <pre>
    +     *    String name
    +     *    int age
    +     * </pre>
    +     *
    +     * @return a string describing this record component
    +     */
    +    public String toString() {
    +    }
    +
    +    /**
    +     * Return the record class which declares this record component.
    +     *
    +     * @return The record class declaring this record component.
    +     */
    +    public Class<?> getDeclaringRecord() {
    +    }
    +}
    diff -r 5573a7098439 src/java.base/share/classes/java/lang/runtime/ObjectMethods.java
    --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    +++ b/src/java.base/share/classes/java/lang/runtime/ObjectMethods.java	Mon Nov 25 16:16:40 2019 -0500
    @@ -0,0 +1,375 @@
    +package java.lang.runtime;
    +
    +import java.lang.invoke.ConstantCallSite;
    +import java.lang.invoke.MethodHandle;
    +import java.lang.invoke.MethodHandles;
    +import java.lang.invoke.MethodType;
    +import java.lang.invoke.TypeDescriptor;
    +import java.security.AccessController;
    +import java.security.PrivilegedAction;
    +import java.util.Arrays;
    +import java.util.HashMap;
    +import java.util.List;
    +import java.util.Objects;
    +
    +/**
    + * {@preview Associated with records, a preview feature of the Java language.
    + *
    + *           This class is associated with <i>records</i>, a preview
    + *           feature of the Java language. Preview features
    + *           may be removed in a future release, or upgraded to permanent
    + *           features of the Java language.}
    + *
    + * Bootstrap methods for state-driven implementations of core methods,
    + * including {@link Object#equals(Object)}, {@link Object#hashCode()}, and
    + * {@link Object#toString()}.  These methods may be used, for example, by
    + * Java&trade; compiler implementations to implement the bodies of {@link Object}
    + * methods for record classes.
    + *
    + * @since 14
    + */
    +@jdk.internal.PreviewFeature(feature=jdk.internal.PreviewFeature.Feature.RECORDS,
    +                             essentialAPI=false)
    +public class ObjectMethods {
    +    /**
    +     * Bootstrap method to generate the {@link Object#equals(Object)},
    +     * {@link Object#hashCode()}, and {@link Object#toString()} methods, based
    +     * on a description of the component names and accessor methods, for either
    +     * {@code invokedynamic} call sites or dynamic constant pool entries.
    +     *
    +     * For more detail on the semantics of the generated methods see the specification
    +     * of {@link java.lang.Record#equals(Object)}, {@link java.lang.Record#hashCode()} and
    +     * {@link java.lang.Record#toString()}.
    +     *
    +     *
    +     * @param lookup       Every bootstrap method is expected to have a {@code lookup}
    +     *                     which usually represents a lookup context with the
    +     *                     accessibility privileges of the caller. This is because
    +     *                     {@code invokedynamic} call sites always provide a {@code lookup}
    +     *                     to the corresponding bootstrap method, but this method just
    +     *                     ignores the {@code lookup} parameter
    +     * @param methodName   the name of the method to generate, which must be one of
    +     *                     {@code "equals"}, {@code "hashCode"}, or {@code "toString"}
    +     * @param type         a {@link MethodType} corresponding the descriptor type
    +     *                     for the method, which must correspond to the descriptor
    +     *                     for the corresponding {@link Object} method, if linking
    +     *                     an {@code invokedynamic} call site, or the
    +     *                     constant {@code MethodHandle.class}, if linking a
    +     *                     dynamic constant
    +     * @param recordClass  the record class hosting the record components
    +     * @param names        the list of component names, joined into a string
    +     *                     separated by ";", or the empty string if there are no
    +     *                     components. Maybe be null, if the {@code methodName}
    +     *                     is {@code "equals"} or {@code "hashCode"}.
    +     * @param getters      method handles for the accessor methods for the components
    +     * @return             a call site if invoked by indy, or a method handle
    +     *                     if invoked by a condy
    +     * @throws IllegalArgumentException if the bootstrap arguments are invalid
    +     *                                  or inconsistent
    +     * @throws Throwable if any exception is thrown during call site construction
    +     */
    +    public static Object bootstrap(MethodHandles.Lookup lookup, String methodName, TypeDescriptor type,
    +                                   Class<?> recordClass,
    +                                   String names,
    +                                   MethodHandle... getters) throws Throwable {
    +    }
    +}
    diff -r 5573a7098439 src/java.base/share/classes/java/lang/runtime/package-info.java
    --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    +++ b/src/java.base/share/classes/java/lang/runtime/package-info.java	Mon Nov 25 16:16:40 2019 -0500
    @@ -0,0 +1,33 @@
    +/**
    + * The {@code java.lang.runtime} package provides low-level runtime support
    + * for the Java language.
    + *
    + * @since 14
    + */
    +
    +package java.lang.runtime;
    diff -r 5573a7098439 src/java.base/share/classes/java/io/ObjectInputStream.java
    --- a/src/java.base/share/classes/java/io/ObjectInputStream.java	Sat Nov 02 10:02:18 2019 +0000
    +++ b/src/java.base/share/classes/java/io/ObjectInputStream.java	Mon Nov 25 16:16:41 2019 -0500
    @@ -218,6 +220,39 @@
      * Similarly, any serialPersistentFields or serialVersionUID field declarations
      * are also ignored--all enum types have a fixed serialVersionUID of 0L.
      *
    + * @implSpec
    + * <a id="record-serialization"></a>
    + * Records are serialized differently than ordinary serializable or externalizable
    + * objects. The serialized form of a record object is a sequence of values derived
    + * from the record components. The stream format of a record object is the same as
    + * that of an ordinary object in the stream. During deserialization, if the local
    + * class equivalent of the specified stream class descriptor is a record class,
    + * then first the stream fields are read and reconstructed to serve as the record's
    + * component values; and second, a record object is created by invoking the
    + * record's <i>canonical</i> constructor with the component values as arguments (or the
    + * default value for component's type if a component value is absent from the
    + * stream).
    + * Like other serializable or externalizable objects, record objects can function
    + * as the target of back references appearing subsequently in the serialization
    + * stream. However, a cycle in the graph where the record object is referred to,
    + * either directly or transitively, by one of its components, is not preserved.
    + * The record components are deserialized prior to the invocation of the record
    + * constructor, hence this limitation (see
    + * <a href="{@docRoot}/../specs/serialization/serial-arch.html#cyclic-references">
    + * [Section 1.14, "Circular References"</a> for additional information).
    + * The process by which record objects are serialized or externalized cannot be
    + * customized; any class-specific writeObject, readObject, readObjectNoData,
    + * writeExternal, and readExternal methods defined by record classes are
    + * ignored during serialization and deserialization. However, a substitute object
    + * to be serialized or a designate replacement may be specified, by the
    + * writeReplace and readResolve methods, respectively.  Any
    + * serialPersistentFields field declaration is ignored. Documenting serializable
    + * fields and data for record classes is unnecessary, since there is no variation
    + * in the serial form, other than whether a substitute or replacement object is
    + * used. The serialVersionUID of a record class is 0L unless explicitly
    + * declared. The requirement for matching serialVersionUID values is waived for
    + * record classes.
    + *
      * @author      Mike Warres
      * @author      Roger Riggs
      * @see java.io.DataInput
    diff -r 5573a7098439 src/java.base/share/classes/java/io/ObjectOutputStream.java
    --- a/src/java.base/share/classes/java/io/ObjectOutputStream.java	Sat Nov 02 10:02:18 2019 +0000
    +++ b/src/java.base/share/classes/java/io/ObjectOutputStream.java	Mon Nov 25 16:16:41 2019 -0500
    @@ -150,6 +150,10 @@
      * defaultWriteObject and writeFields initially terminate any existing
      * block-data record.
      *
    + * @implSpec
    + * Records are serialized differently than ordinary serializable or externalizable
    + * objects, see <a href="ObjectInputStream.html#record-serialization">record serialization</a>.
    + *
      * @author      Mike Warres
      * @author      Roger Riggs
      * @see java.io.DataOutput

Additional links
-------

* javadoc: http://cr.openjdk.java.net/~vromero/records.review/CSRs/core-libs/javadoc.05/java.base/module-summary.html
* specdiff: http://cr.openjdk.java.net/~vromero/records.review/CSRs/core-libs/specdiff.05/overview-summary.html
* serialization spec: https://cr.openjdk.java.net/~chegar/records/spec/records-serialization.07.html
Comments
JDK-8233436 "Discuss evolution of records in serialization spec" - has been filed to address the follow-up Serialization work.
01-07-2020

The most significant aspect of serialization evolution concerning records is the compatible scenarios where an ordinary class may be changed to a record class. The following is a proposal to address that: ( adding, removing, etc, fields/components is fraught with semantic danger, just like adding a constant to an enum (it is not necessarily compatible) ) ### 5.6.2 Compatible Changes The compatible changes to a class are handled as follows: - ... - Changing a class from an ordinary class to a record class - A class that is suitable to be converted from an ordinary class to a record class, and relies on default serialization, may be changed to a record class. The ordinary class must have `java.lang.Object` as its direct superclass. The name and type of the record class's components must match that of the name and type of the ordinary class's serializable fields. Record objects are reconstructed through the record class's canonical constructor. If the canonical constructor throws an exception, say while checking invariants, then an `InvalidObjectException` is thrown. - Changing a class from a record class to an ordinary class - A record class that relies on default serialization, may be changed to an ordinary class. The ordinary class must declare an explicit `serialVersionUID`, whose value is the same as that of the prior record class's `serialVersionUID`. The name and type of the ordinary class's serializable fields must match that of the name and type of the prior record class's components.
26-11-2019

I checked that the essential APIs marked in this CSR and the JLS changes in JDK-8233433 are consistent. For the serialization spec, having the explicit mention of InvalidClassException is good. However, the docs for InvalidClassException were not updated to account for this change. I filed JDK-8234781 and sent a change out for review to correct an existing oversight and to add a catch-all clause to cover records and any future changes. No further action. On that point, no further action is needed for this CSR. However, I think it is important to discuss the topic of the envisioned and allowable evolution of records vis a vis serialized and I filed JDK-8234782 to track this point later in JDK 14. Likewise, I think the spec of Records.equals would benefit from some further refinement; please address JDK-8234783 later in JDK 14. Moving this CSR to Approved subject to the follow-up work noted above.
26-11-2019

[~darcy] regarding your previous comment: If find the current spec of Record.equals to be ambiguous in its wording "and [use] the primitive wrapper equality comparison for components whose types are primitive types." Does that mean "wrap each primitive and use == to compare them" or "(conceptually) wrap each primitive and then call wappedA.equals(wrappedB)." I recommend basing the semantics on the two argument compare methods in each primitive wrapper class. Sample wording, after breaking the implSpec into multiple sentences, "... and for components that are of a primitive type, for the corresponding primitive wrapper class PW, use PW.compare(a, b) == 0 to determine equality." I have changed the referred comment to: and according to the semantics of the {@code equals} method on the corresponding primitive wrapper type. as we don't want to commit to actually boxing the primitive and calling equals, we want to keep some freedom here
25-11-2019

The serialization spec includes a section on type evolution: https://docs.oracle.com/en/java/javase/13/docs/specs/serialization/version.html#type-changes-affecting-serialization This section explicitly mentions enum: > Changing a class from a non-enum type to an enum type or vice versa > since the stream will contain data that is incompatible with the > implementation of the available class IMO it is incomplete to have no mention of records, even if just so say "all the class rules still apply".
25-11-2019

comments from Chris: For the serialization work, in ���The process by which record objects are serialized cannot be customized;��� I���d prefer to see ���...are serialized or externalized cannot be customized.��� ������ or externalized ������ has been added. ���The record object will be constructed by an invocation of its canonical constructor. The canonical constructor for record class, R, is found by, first building a method descriptor from the number, order, and declared types of the record R���s components, as returned by R::getRecordComponents; and then locating R���s declared constructor matching the descriptor. If the canonical constructor cannot be found, an exception is thrown.��� Which exception is thrown? The rest of the serial spec is explicit in detailing particular exceptions for different circumstances. InvalidClassException is thrown - spec has been updated. The serialization spec should also at least briefly discuss how record types can be compatibly evolved, as is done for other kind of classes. Serialization can handle more evolution than the JLS for record evolution. I���d prefer to not suggest that in this document.
25-11-2019

[~darcy] I already tried to attach a pdf file in the past with the same internal error message from JIRA. For some reason it is not working in .html or .pdf format. The weird thing is that I have been able to attach pdfs to other JIRA issues, not sure why it is not working in this case.
24-11-2019

Attachment <Record Serialization (Preview)_v6.pdf> anti-virus scan found a possible virus. The system has removed attachment. Please check the file before attempting to upload it again
24-11-2019

Please attempt to render the seriaization spec in some other format, say a pdf file, and attach that. The specdiffs don't look to be generated using the full set of JDK taglets so the implSpec, implNote, and preview text doesn't appear to be present, bounding the utility of the spec diff. I believe some additional refinement is needed before this request can be approved. For the Object methods in java.lang.Record, I recommend *not* inheritdoc-ing the specs from Object; rather, use some vague but true statement for the main spec. In other API work I've used ""Obeys the general contract of Object.hashCode." for hashCode; the specific implSpec text would be retained. If find the current spec of Record.equals to be ambiguous in its wording "and [use] the primitive wrapper equality comparison for components whose types are primitive types." Does that mean "wrap each primitive and use == to compare them" or "(conceptually) wrap each primitive and then call wappedA.equals(wrappedB)." I recommend basing the semantics on the two-argument compare methods in each primitive wrapper class. Sample wording, after breaking the implSpec into multiple sentences, "... and for components that are of a primitive type, for the corresponding primitive wrapper class PW, use PW.compare(a, b) == 0 to determine equality." Likewise, for hashCode, "...and for components that are of a primitive type, for the corresponding primitive wrapper class PW, use PW.hashCode(value) ..." In + /** + * Returns an {@code AnnotatedType} that represents the use of a type to + * specify the annotated type of this record component. + * + * @return an object representing the declared type of this record component + */ + public AnnotatedType getAnnotatedType() { + } + The returned type isn't necessarily annotated. Please match the wording structure used for Field.getAnnotatedType() and Parameter.getAnnotatedType() which avoid this implication. For the bootstrap method, if the lookup parameter can be ignored, the spec should say it is ignored, can be null, etc. For the serialization work, in "The process by which record objects are serialized cannot be customized;" I'd prefer to see "...are serialized or externalized cannot be customized." In "The record object will be constructed by an invocation of its canonical constructor. The canonical constructor for record class, R, is found by, first building a method descriptor from the number, order, and declared types of the record R's components, as returned by R::getRecordComponents; and then locating R's declared constructor matching the descriptor. If the canonical constructor cannot be found, an exception is thrown." Which exception is thrown? The rest of the serial spec is explicit in detailing particular exceptions for different circumstances. The serialization spec should also at least briefly discuss how record types can be compatibly evolved, as is done for other kind of classes. Marking the request as pended; please re-finalized when updated.
22-11-2019

[~darcy] yep sorry for that, I have updated the links to the last version of the javadoc and the specdiff
20-11-2019

[~vromero], the .02 version of the specdiff and javadoc are not consistent with the diff given in the specification section. For example, the isRecord text + ....Note that class {@link Record} is not a + * record type and thus invoking this method on class {@link java.lang.Record} + * returns {@code false}. is only present in the in-line specification section above. Please provide a consistent set of artifacts for the review.
20-11-2019

Regarding the `lookup` parameter, Vicente is correct: all bootstraps require a lookup parameter, which is automatically stacked by the VM during `invokedynamic` dispatch, and anyone who is in the mind to call a bootstrap method already understands the role of the lookup. So I do not thing further clarification is required.
20-11-2019

[~darcy] I have updated the spec with changes from me and Chris trying to cover all of your comments. I couldn't attach the new version of the spec to this JIRA entry as the systems thinks my docs have a virus but I have updated the link to the last version of the serialization spec.
19-11-2019

[~darcy] in a previous comment you said: + * for components + * whose types are reference types, and the primitive wrapper equality + * comparison for components whose types are primitive types. The statement ahove is not true/desirable for float and double. The wrapper class Float/Double may need special handling too. I think that we are using the wrapper classes Float / Double to do comparisons even in value types. What is exactly the special handling that they could need? also in the same comment you said: ObjectMethods needs more detail, what is lookup, what are the semantics of the generated methods? I believe that the description of the lookup is compatible with the description for lookup parameters in APIs like j.l.i.LambdaMetafactory. With the difference that the `lookup` parameter is just ignored in ObjectMethods::bootstrap, so what else can we say about the lookup that can add any value? It is just ignored. We can say in the corresponding spec something in the lines of: This parameter is here just because every bootstrap method has to have a lookup parameter, which usually represent a lookup context with the accessibility privileges of the caller. But it is just ignored in this method. regarding your comment about the semantics of the generated method I have added this comment to the spec: For more detail on the semantics of the generated methods see the specification * of {@link java.lang.Record#equals(Object)}, {@link java.lang.Record#hashCode()} and * {@link java.lang.Record#toString()}.
19-11-2019

Moving to Provisional. For + * @jls 8.10 please add the anticipated section name as is now customary for @jls tags. + * for components + * whose types are reference types, and the primitive wrapper equality + * comparison for components whose types are primitive types. The statement ahove is not true/desirable for float and double. The wrapper class Float/Double may need special handling too. In java.lang.Record, the text in implNote's reads as normative requirements so should be under a implSpec tag instead. Please also add some reference to the special serial behavior of records to the `java.lang.Record` spec. As a code review comment, I prefer to explicitly see the {@inhertiDoc} for a method's spec. For RecordComponent.getDeclaringRecord, it might be possible to declare the return type to be something like `Class<? extends Record>`, but that might not be very helpful in practice `ObjectMethods` needs more detail, what is lookup, what are the semantics of the generated methods? It would be possible for the special treatment of records be done only for serializable records that are not *externalizable*. If the intention is to restrict both cases, the wording of the spec could be improved to make that explicit. The discussion of the serialization of records is normative text and not just an apiNote, even if the text is mostly redundant with text in the serialization spec.
19-11-2019

All enum types are serializable since java.lang.Enum is serializable hence no explicit guidance is needed on how to handle a serializable enum. (Although it would be reasonable to state that since enums get special handling, serialization related methods are ignored; I filed JDK-8234381 as a follow-up.) I think it would be sufficient to add something like "Records have special serialization which is explained in more detail in ..."
19-11-2019

[~darcy] in a comment above you said: `Please also add some reference to the special serial behavior of records to the java.lang.Record spec.` it seems to me that referring to serialization of records at java.lang.Record is a bit out of place as not all records are serializable by default. Are you proposing to basically reproduce the text in ObjectInputStream in the Record class? or just add a small comment with a link like: `Records have special serialization which is explained in more detail in: {@link java.io.ObjectInputStream ObjectInputStream}`? Also `enums`, always referred to as `records` similar soul, have special serialization but there is no reference to this at class java.lang.Enum
18-11-2019

PS Please have Class.isRecord explicitly state that java.lang.Record is not itself a record type (just as java.lang.Enum is not an enum type).
14-11-2019

Adjusting risk estimate to "low" since the name "Record" is being added to java.lang and can be a source compatibility issue with code using existing types named "Record".
14-11-2019

sure please feel free to edit the CSR. [~alanb] I updated the title to the one you proposed. I will also remove the throws from getRecordComponents. Thanks
06-11-2019

A minor comment on getRecordComponents is that SecurityException is a not a checked exception so the "throws SecurityException" in the method declaration looks a bit odd. Otherwise the API additions to java.base and the apiNote in ObjectOutputStream look good to me. Vicente - do you want us to edit the CSR or just write comments here? I think we minimally need to rename the CSR to replace "Core-libs" with APIs or Preview APIs.
06-11-2019

[~darcy] [~alanb] I have updated the spec plus the links. Also Chris reminded me of the serialization API so this last iteration includes it plus a link to the last version of the serialization spec. I have also updated the spec of Class::isRecord. I have also made some modifications to java.lang.reflect.RecordComponent: I have removed method ::toGenericString, added new method ::getDeclaringRecord and modified the spec of ::toString removing the record name from the output. I believe the only open issue so far is if the spec of ObjectMethods should be enhanced reusing the spec of java.lang.Record
05-11-2019

[~darcy] those details were added to the API of java.lang.Record, for each of the methods: equals, toString and hashCode. I can adapt that API and reuse it in ObjectMethods, if that sounds good to you
05-11-2019

[~vromero], there are many ways one could write an equals method. What are the semantics for how ObjectMethod code does this? For example, in the code review thread there was discussion on how floating-point numbers were compared. Likewise, for hashCode, presumably a constant 42 is not unconditionally returned. A spec mentioning something like "a hash is computed using the value of each component" seems appropriate. And so on.
05-11-2019

[~darcy] thanks for your comments, I agree with the change you propose to Class::isRecord. Regarding the comment about ElementType, I added a reference to the sections in the JLS that explains the rules for annotations during compile time in records. The rules are pretty long, complicated and spread through several sub-sections of the spec. I really don't see very clear how to add all of that unless we add a very simple and probably incomplete description. Regarding the comments on j.l.r.RecordComponent, I have received comments in the opposite direction. I have sent an email to a broader public to try to get a consensus on this. Regarding java.lang.runtime.ObjectMethods sorry but it is not very clear to me what your expectations are. Would you like to see a better description about how the implementation works?
05-11-2019

For Class.isRecord, I recommend augumenting true if and only if this class is a record class with "; false otherwise." wording used in several other predicate methods in java.lang.Class. I recommend ElementType contain at least some reference to the special annotation compile-time annotation rules for records. For the expected uses of RecordComponent.toString omitting the record name may be more useful. Compare with Parameter.toString. java.lang.reflect.RecordComponent doesn't seem to have a "getDeclaringRecord" kind of method. In java.lang.runtime.ObjectMethods, it is not clear to me how the lookup argument should be used. I was expecting more specifications about the operational semantics of toString/equals/hashCode.
05-11-2019