JDK-8233433 : Compiler implementation for records
  • Type: CSR
  • Component: tools
  • Sub-Component: javac
  • Priority: P3
  • Status: Closed
  • Resolution: Approved
  • Fix Versions: 14
  • Submitted: 2019-11-03
  • Updated: 2019-12-11
  • Resolved: 2019-12-03
Related Reports
CSR :  
Relates :  
Description
Summary
-------

Add _record classes_ to the Java language. Record classes, a.k.a. records, allow the definition of shallowly immutable, transparent carriers for a fixed set of values, the record components. 

Problem
-------

Modeling data classes with the Java language is possible but users have to generate a lot of boilerplate to model even the simplest of them. There is also a lot of repetition implying a lot of places for bugs to hide. Users are forced to provide implementations for methods like: toString, equals and hashCode even when those implementations could be directly derived from the state of transparent data carriers and easy to generate automatically by a compiler.

Solution
--------

The Java language will be enhanced by supporting record classes: a new construction for the definition of shallowly immutable, transparent carriers for a fixed set of values, its record components. These feature will constitute a preview feature ([JEP 12](http://openjdk.java.net/jeps/12)) in Java SE 14.

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

    diff -r fd51f02ef75c -r cba5b68a6424 src/jdk.compiler/share/classes/com/sun/source/tree/ClassTree.java
    --- a/src/jdk.compiler/share/classes/com/sun/source/tree/ClassTree.java Mon Nov 18 16:30:21 2019 -0500
    +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/ClassTree.java Mon Nov 18 16:40:42 2019 -0500
    @@ -29,7 +29,7 @@
     import javax.lang.model.element.Name;
     
     /**
    - * A tree node for a class, interface, enum, or annotation
    + * A tree node for a class, interface, enum, record, or annotation
      * type declaration.
      *
      * For example:
    @@ -44,6 +44,7 @@
      *
      * @jls 8.1 Class Declarations
      * @jls 8.9 Enum Types
    + * @jls 8.10 Record Types
      * @jls 9.1 Interface Declarations
      * @jls 9.6 Annotation Types
      *
    diff -r fd51f02ef75c -r cba5b68a6424 src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java
    --- a/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java      Mon Nov 18 16:30:21 2019 -0500
    +++ b/src/jdk.compiler/share/classes/com/sun/source/tree/Tree.java      Mon Nov 18 16:40:42 2019 -0500
    @@ -644,6 +644,20 @@
             PROVIDES(ProvidesTree.class),
     
             /**
    +         * {@preview Associated with records, a preview feature of the Java language.
    +         *
    +         *           This enum constant 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.}
    +         *
    +         * Used for instances of {@link ClassTree} representing records.
    +         *
    +         * @since 14
    +         */
    +        RECORD(ClassTree.class),
    +
    +        /**
              * Used for instances of {@link RequiresTree} representing
              * requires directives in a module declaration.
              */

In addition, proposed changes to the JLS are available at http://cr.openjdk.java.net/~gbierman/jep359/jep359-20191125/specs/records-jls.html
Comments
Re-approving amended request.
03-12-2019

A condition is missing in JLS 8.10.3: An accessor method must not be declared `static`. I have updated the spec to reflect this change (a single line) at: http://cr.openjdk.java.net/~gbierman/jep359/jep359-20191125/specs/records-jls.html. I have attached a pdf of the spec to this page. I have moved to Finalized, and request Approval. gbierman@gbierman-mac:~/Depots/closed$ hg diff -c 30704:012cc50cb363 diff -r 9f5a21334115 -r 012cc50cb363 closed/src/java.se/share/specs/records-jls.md --- a/closed/src/java.se/share/specs/records-jls.md Thu Nov 28 14:06:43 2019 +0000 +++ b/closed/src/java.se/share/specs/records-jls.md Tue Dec 03 11:27:03 2019 +0000 @@ -2433,6 +2433,8 @@ - The accessor method must be declared `public` + - The accessor method must not be declared `static` + - The accessor method must not have a `throws` clause - All other rules for a method in a normal class declaration must be
03-12-2019

Confirming updated version still approved; thanks.
27-11-2019

Thanks Joe. Alex asked for some minor stylistic changes on the spec-experts list. These have been made to the online version. I attach an updated pdf for the record (pun not intended).
26-11-2019

[~darcy] thanks
26-11-2019

Thanks for the update. Moving to Approved.
26-11-2019

Thanks Joe. I've made a new version available at: http://cr.openjdk.java.net/~gbierman/jep359/jep359-20191125/specs/records-jls.html > For 3.8 Identifiers, I suggest a "neither .. nor ..." construction to list the excluded name. Thanks! Done. > Note that JEP 361 for switch expressions is already integrated in JDK 14. I understand your point but there isn't a JLS14 yet for me to rebase. I'm going to leave this as I think it's really a process bug that is orthogonal to records. (And the potential conflict is pretty clear in this particular case.) > In 6.5 "Determining the Meaning of a Name" a 17th context is not listed. Doh! Now fixed. > In "An implicitly declared public accessor method with the same name as the record component, whose return type is the declared type of the record component, unless a public method with the same signature is explicitly declared in the body of the declaration of R." does that imply a "covariant override" accessor can be explicitly defined as the signature does not include the return type? For example if the component had a type of Object and the user defined an accesssor method with a type of String? Would a bridge method need to be defined? Should the return type be constrained to match too? This is a very good question. I've actually changed the description of accessor methods and constructor methods to make this clearer (I hope). In essence there is a two stage process. For accessor methods: If you have the name of a component and an empty formal parameter list, then you are considered to be an accessor method for the component. We then ask additional criteria of you - it's a compile-time error if you don't satisfy them! This includes having a return type that is identical to the derived type of the record component, being `public`, etc. It's essentially the same for constructors: a record component list essentially derives a canonical constructor signature. If you are a constructor that is override-equivalent, then you are a canonical constructor. We then ask additional criteria of you, being `public`, not being generic, etc. These definitions clear up a lot of corner cases, and more importantly lead to better error messages. (In your particular example, the `String` returning method would *not* be an accessor for the `Object` component, so one would be implicitly declared for you, and then you'd get an error as we'd have two overloads with identical signatures). > Marking as pended until this minor issues are addressed. Please refinalize when ready, attaching a new version of the spec (leaving the old one in place) and updating the information URL at the bottom of the specification section. I attach the pdf and will refinalize.
25-11-2019

For 3.8 Identifiers, I suggest a "neither .. nor ..." construction to list the excluded name. Note that JEP 361 for switch expressions is already integrated in JDK 14. In 6.5 "Determining the Meaning of a Name" a 17th context is not listed. In "An implicitly declared public accessor method with the same name as the record component, whose return type is the declared type of the record component, unless a public method with the same signature is explicitly declared in the body of the declaration of R." does that imply a "covariant override" accessor can be explicitly defined as the signature does not include the return type? For example if the component had a type of Object and the user defined an accesssor method with a type of String? Would a bridge method need to be defined? Should the return type be constrained to match too? Marking as pended until this minor issues are addressed. Please refinalize when ready, attaching a new version of the spec (leaving the old one in place) and updating the information URL at the bottom of the specification section.
22-11-2019

An updated copy of the JLS spec has been placed at: http://cr.openjdk.java.net/~gbierman/jep359/jep359-20191121/specs/records-jls.html To your points: > Since switch expressions have now been pushed, before this request is finalized, please rebase the JLS to take that into account. In particular, the not-quite-identifier list in 3.8 "Identifiers" needs to be merged. My understanding is that the spec should be rebased to JDK13 - which I have now done - so it wouldn���t contain the material from JEP 361. However, I have added an editorial comment that JEP 361 is proposing to extend the set of restricted identifiers to include ���yield���. I hope this suffices. >Given that the records section alludes to serialization, perhaps deserialziation of records could be mentioned in the following paragraph: "Constructors are invoked by class instance creation expressions (15.9), by the conversions and concatenations caused by the string concatenation operator + (15.18.1), and by explicit constructor invocations from other constructors (8.8.7). Access to constructors is governed by access modifiers (6.6), so it is possible to prevent class instantiation by declaring an inaccessible constructor (8.8.10).��� We no longer make reference to serialization in normative text. > For 8.10.1 Record Header, I suggest adding an informative note that "The forbidden name are the names of methods on Object and and subset of the serialization-related methods." This strikes me as worthy of a design explanation. We have changed the restrictions around record component names. The restriction is better given in the section concerning the implicit accessor methods (it turns out a similar restriction already exists for default methods in interfaces). But I have added some discussion in the section on record components to make it clearer. > For 8.10.3 "Record Members", if the record implements an interface with a default method, say "Foo foo()" and there is a record component "Foo foo", the mandated accessor method will get generated by the compiler. Is this worth noting explicitly even if only in a comment? Are there interactions to look out for, such as an accessor method being a covariant override of a default method from an interface? Well, the accessor methods are subject to the normal rules for methods in classes, as are all record methods. I have made this more general point explicitly, which I hope addresses your point. > I suggest some explicit discussion of constructor receiver parameters with respect to records. For example, given "This is a public constructor whose formal parameter list is identical to the record header of R. A canonical constructor can be explicitly declared in the body of the declaration of R." presumably a constructor with a preceding receiver parameter ahead of the the "formal parameter list matching..." would still count as a canonical constructor. At least in the JLS, the formal parameter list is distinct from the receiver parameter. But I think this case is moot, as it is only possible to declare a receiver parameter in a constructor for a non-static nested class. All nested record types are implicitly static, so it isn���t possible. > Note that the NonNegativePoint example is semantically buggy since Math.abs(Integer.MIN_VALUE) is Integer.MIN_VALUE. Thanks, that example has been fixed. > Should the annotations on the record component, subject to applicability, also be copied down to the parameters of the canonical constructor if implicitly generated? Thanks. I have clarified the role of annotations in the constructors. Here���s the basics: The formal parameter list of the canonical constructor has the *same signature* as the record component list (i.e. the same names and types, don���t care about the annotations). If one is implicitly declared then only the applicable annotations are copied down from the record component list (I call this process ���derived from��� in the spec). Clearly for the canonical constructor, only the applicable annotations are copied down to the implicitly declared formal parameter list.
21-11-2019

[~vromero], the JLS spec issues noted above should be handled before this CSR is finalized.
19-11-2019

[~darcy] do we need to wait for the spec to be updated as you requested for the CSR to be finalized or can we move on after the tree API updates I did
18-11-2019

Since switch expressions have now been pushed, before this request is finalized, please rebase the JLS to take that into account. In particular, the not-quite-identifier list in 3.8 "Identifiers" needs to be merged. Given that the records section alludes to serialization, perhaps deserialziation of records could be mentioned in the following paragraph: "Constructors are invoked by class instance creation expressions (15.9), by the conversions and concatenations caused by the string concatenation operator + (15.18.1), and by explicit constructor invocations from other constructors (8.8.7). Access to constructors is governed by access modifiers (6.6), so it is possible to prevent class instantiation by declaring an inaccessible constructor (8.8.10)." For 8.10.1 Record Header, I suggest adding an informative note that "The forbidden name are the names of methods on Object and and subset of the serialization-related methods." This strikes me as worthy of a design explanation. For 8.10.3 "Record Members", if the record implements an interface with a default method, say "Foo foo()" and there is a record component "Foo foo", the mandated accessor method will get generated by the compiler. Is this worth noting explicitly even if only in a comment? Are there interactions to look out for, such as an accessor method being a covariant override of a default method from an interface? I suggest some explicit discussion of constructor receiver parameters with respect to records. For example, given "This is a public constructor whose formal parameter list is identical to the record header of R. A canonical constructor can be explicitly declared in the body of the declaration of R." presumably a constructor with a preceding receiver parameter ahead of the the "formal parameter list matching..." would still count as a canonical constructor. Note that the NonNegativePoint example is semantically buggy since Math.abs(Integer.MIN_VALUE) is Integer.MIN_VALUE. Should the annotations on the record component, subject to applicability, also be copied down to the parameters of the canonical constructor if implicitly generated? For the tree API, to summarized an external discussion, before the request is finalized, please update it to at least support RECORD as described. Moving to Provisional.
14-11-2019

[~darcy], by same measure, what good use is there to know that something is an enum if I can't get to its constants (not even the constant names) ? I think supporting RECORD kind is very different from supporting RECORD_COMPONENT, and I'm wary of any argument which says that one should imply the other; as I said there are technical reasons as that cannot currently be the case. So, can we perhaps agree to (partially) fix support to add a RECORD kind? In other words, provide (at least initially) same level of support as for enums. More specifically, what I'm suggesting is to add a new Tree.Kind enum constant called RECORD, backed by the ClassTree interface, and then to tweak JCClassDecl in JCTree to add support for record, as follows ``` @DefinedBy(Api.COMPILER_TREE) public Kind getKind() { if ((mods.flags & Flags.ANNOTATION) != 0) return Kind.ANNOTATION_TYPE; else if ((mods.flags & Flags.INTERFACE) != 0) return Kind.INTERFACE; else if ((mods.flags & Flags.ENUM) != 0) return Kind.ENUM; else if ((mods.flags & Flags.RECORD) != 0) return Kind.RECORD; else return Kind.CLASS; } ``` As for your desire for being able to retrieve record component *trees* using the API, I'm afraid that, as said above, this can't be implemented because, simply, there's no tree. Record components exist in the language model, so you can get an Element from a record tree using the Trees::getElement(TreePath) API, and work things from there, which, while less direct, doesn't seem completely horrible either.
08-11-2019

[~mcimadamore], I think minimal updates would be for ClassTree to mention "record" as one of the kinds of things being covered and for Tree.Kind to get RECORD and RECORD_COMPONENT values. As much of the syntactic (and semantic!) weight of a record can be conveyed by its components rather than its explicit declarations, it does strike me as incomplete to not provide some way of getting them via the tree API.
08-11-2019

[~darcy] - what you ask, which is sensible, is hard due to the fact that record components, as enum constants, are desugared at parse time, so there's no real AST to back them up to be exposed via the tree API. Since ClassTree doesn't have a 'getEnumConstants' method, I don't think there is a "need" for a "getRecordComponents" either. Of course it can be added, with time, assuming the right way to add it is found. But is probably to address both records and enums at the same time, by doing a bigger revamp of the code which avoids the eager desugaring - which is something we have discussed many times in the past - but it just takes time.
07-11-2019

[~vromero], isn't there a need for a record-component-tree at least in the com.sun.source?
07-11-2019

[~darcy] right now there are no changes in com.sun.source related to records. We can and will do some experiments to add that support but not sure if we will have the time to have all of this ready before the deadline for this feature to be previewed. I wonder if this is something we can provide while the feature is in preview or if this is a showstopper.
07-11-2019

The javax.lang.model changes are being handled under a separate CSR also under review. However, there needs to be CSR review of the tree API changes. [~vromero], which CSR is going to cover the com.sun.source changes? This CSR would be an appropriate one to cover those changes; filing a separate one isn't necessary. Thanks [~jlahoda].
06-11-2019

There should presumably also be a javadoc diff.
04-11-2019

Hi [~vromero], before the request is proposed or finalized for CSR review, please attach or place in-line some representation of the spec change. While a reference to a URL is a helpful supplement, it is not sufficient for the archival purposes of the CSR.
04-11-2019