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.
Yes, I know, and I'd like to eventually clear this one too, it's a
gratuitous restriction (though there is a VM component, which makes it
more work.)
I deliberately chose not to include this one in this proposed batch,
because there was no credible connection to records. But, the
connection to sealed types might be just enough to justify it as part of
that project.
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.