Finalizing in JDK 16 - Records

Remi Forax forax at univ-mlv.fr
Mon Aug 3 21:59:37 UTC 2020


> De: "Brian Goetz" <brian.goetz at oracle.com>
> À: "Gavin Bierman" <gavin.bierman at oracle.com>, "amber-spec-experts"
> <amber-spec-experts at openjdk.java.net>
> Envoyé: Lundi 3 Août 2020 19:20:09
> Objet: Re: Finalizing in JDK 16 - Records

> 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.
There is another restriction that doesn't make a lot of sense, an interface can not declare a member record (or a class, an enum, etc) as private. 

This restriction also interacts poorly with sealed interface with implicit permits directive. 

sealed interface I { 
int value(); 

private record Impl(int value) implements I { } // does not compile 

public static I wrap(int value) { 
return new Impl(value); 
} 
} 

I propose to relax this restriction as part of Sealed Preview 2. 

Rémi 

> 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/52783335/attachment-0001.htm>


More information about the amber-spec-experts mailing list