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