CSR :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
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™ 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™ + * 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™ 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™ 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
|