Finalizing in JDK 16 - Records

Brian Goetz brian.goetz at oracle.com
Mon Aug 3 17:20:09 UTC 2020


One of the less-well-known "featurelets" that is buried in records has 
to do with nesting.  We want records to nest cleanly in methods (local 
records) and classes.  During the second preview, we made some progress, 
but not complete progress, on this.

As of Preview/2, the following are allowed:

  - Records can be nested in top-level (non-inner) classes.  They are 
implicitly static.
  - Records can be nested in methods (local records).  They are 
implicitly static.  (The spec now gives meaning to the notion of a 
static local class, whether or not you can explicitly declare one.)

At the same time as we did the latter, we removed the restrictions from 
local interfaces and enums too.  This is what we call a "horizontal" 
move; we could have stuck strictly to local records, which some might 
argue is more in line with the JEP's mandate, but the result would be 
that it leaves the language more irregular: why concrete classes and 
records, but not enums (on which the design of records is modeled?)  It 
made more sense to allow all of these (or at least, move towards 
allowing all of them) in rather than leave an arbitrary trail of ad-hoc 
"X and Y but not Z" constraints.  And like records, local enums and 
interfaces are implicitly static.  This is all in Preview/2.

There is still one restriction we would like to bust: you can't have 
records (today) in inner classes because they are implicitly static, and 
there is a blanket restriction against static members (explicit or 
implicit) in inner classes.  As above, we could drill the smallest hole 
possible and just allow records in there, but it makes more sense (and 
will be easier for users to reason about) if we just drop the "no 
statics in inner" rule.  As has been stated before, this rule was added 
in 1.1 out of an "abundance of caution", and I think it is time to 
retire it in entirety.  So I would like the next round of record spec to 
permit records, enums, interfaces, static classes, and static fields in 
inner classes.  With refactoring in the spec that has already been done 
for Preview/2, this is largely a matter of removing these restrictions 
from the spec, implementations, and tests.


On 7/27/2020 6:54 AM, Gavin Bierman wrote:
> [Second email of two, looking to close out features we hope to finalize in JDK
> 16.]
>
>
> Records
> -------
>
> Record classes are a special kind of class that are used primarily to define a
> simple aggregate of values.
>
> Records can be thought of as _nominal tuples_; their declaration commits to a
> description of their state and given that their representation, as well as all
> of the interesting protocols an object might expose -- construction, property
> access, equality, etc -- are derived from that state description.
>
> Because we can derive everything from a common state description, the
> declaration can be extremely parsimonious. Here is an example of a record class
> declaration:
>
>      record Point(int x, int y){}
>
> The state or, more formally, a record component list, (int x, int y), drives the
> implicit declaration of a number of members of the Point class.
>
> - A `private` field is declared for each record component
> - A `public` accessor method is declared for each record component
> - A constructor is declared with an argument list matching the record component
> list, and whose body assigns the fields with the corresponding argument. This
> constructor is called the _canonical constructor_.
> - Implementations of the methods: equals, toString and HashCode.
>
> The body of a record class declaration is often empty, but it can contain method
> declarations as usual. Indeed, if it is necessary, the implicitly declared
> members - the accessors, canonical constructor, and equals, toString, or
> HashCode methods -- can alternatively be explicitly declared in the body.
>
> Often the reason for explicitly providing a canonical constructor for a record
> class is to validate and/or normalize the argument values. To
> enhance the readability of record class declarations, we provide a new compact
> form of canonical constructor declaration, where only this
> validation/normalization code is required. Here is an example:
>
>      record Rational(int num, int denom) {
>          Rational {
>              int gcd = gcd(num, denom);
>              num /= gcd;
>              denom /= gcd;
>          }
>      }
>
> The intention of a compact constructor declaration is that only validation
> and/or normalization code need be given in the constructor body; the remaining
> initialization code is automatically supplied by the compiler. The formal
> argument list is not required in a compact constructor declaration as it is
> taken from the record component list. In other words, this declaration is
> equivalent to the following one that uses the conventional constructor form:
>
>      record Rational(int num, int denom) {
>          Rational(int num, int demon) {
>              // Validation/Normalization
>              int gcd = gcd(num, denom);
>              num /= gcd;
>              denom /= gcd;
>              // Initialization
>              this.num = num;
>              this.denom = denom;
>          }
>      }
>
> Once we settled on the design of record classes, things have been pretty stable.
> Three issues that did arise were:
>
> 1. Initially canonical constructors were required to be public. This was changed
> in the second preview. Now, if the canonical constructor is implicitly declared
> then its access modifier is the same as the record class. If it is explicitly
> declared then its access modifier must provide at least as much access as the
> record class.
>
> 2. We have extended the meaning of the `@Override` annotation to include the
> case that the annotated method is an explicitly declared accessor method for a
> record component.
>
> 3. To enforce the intended use of compact constructors, we made it a
> compile-time error to assign to any of the instance fields in the constructor
> body.
>
> One area that has generated a number of questions is annotations. Our intention
> is that an annotation on a record component is propagated to the field,
> accessor, and/or constructor parameter, according to the applicability of the
> annotation. It is not clear what other design choices there are. So we hope this
> is just something that has to be learnt, and afterwards it feels natural.
>
> The records JEP also allows for local record declarations. This is important as
> records will often be used as containers for intermediate data within method
> bodies. Being able to declare these record classes locally is essential to stop
> proliferation of classes. We are aware of some small tweaks that will be
> required to the specification during the second preview period, but overall this
> feature has not generated any controversy.
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20200803/d428ddb4/attachment.htm>


More information about the amber-spec-experts mailing list