> De: "Brian Goetz" <brian.go...@oracle.com> > À: "Gavin Bierman" <gavin.bier...@oracle.com>, "amber-spec-experts" > <amber-spec-experts@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.