Final issues regarding records
Gavin Bierman
gavin.bierman at oracle.com
Mon Apr 6 10:10:55 UTC 2020
Dear Experts:
I’d like to circle back on a couple of issues regarding records. I am finalizing
the draft language spec - would really like to complete this very soon - so it
would be great to get some EG feedback so we cancomplete the spec (and tweak
the implementation as necessary). I'm especially keen to hear from anyone who
has actually been playing with the records prototype already.
#1. Accessibility of various record members.
In an earlier email, I wrote:
> Currently the mandated members are public regardless of the accessibility of
> the record type. What should be the default accessibility for record
> members?
>
> A: Currently the most popular proposal is that mandated members get
> accessibility of the record type. Other members get package level
> accessibility as per a regular class. (Note: it should be legal for explicit
> declarations of mandated members to specify something _more_ accessible
> (same rules as overriding.) )
Just to clarify which members we are talking about; there are actually two
categories to consider:
1. The implicitly declared accessor methods corresponding to the record
components.
2. The canonical constructor (including possibly a compact constructor).
For these members, the current spec and implementation require that they are
`public` regardless of the accessibility of the enclosing record type.
The question before the house is whether this is the right design. A gut
reaction is that it feels wrong; why insist on a public accessibility regardless
of the enclosing declaration? But after a little thought, it is not so clear
that this is the wrong design.
First, note that enums already have this implicit-members-are-`public` feature -
the implicitly declared `values` and `valueOf` methods are `public` regardless
of the accessibility of the enum type. As far as I am aware, this feature of
enums has not caused any confusion (or much comment).
Second, consider the case where we are using interfaces to specify the behavior
of the record type, including the accessor methods. For example:
interface I {
int i();
}
record R(int i) implements I {}
If we make R private, then there is no way we can satisfy our contract as
interfaces can’t have private methods. In this example, there’s not a lot we can
do other than explicitly declare the accessor method ourselves. This is a shame
- we can't utilize the implicit declaration of accessor methods because of a
mismatch of accessibility.
Whilst this example is a little contrived, it asks whether the essence of the
(public) contract that records satisfy, even private ones, is that they support
accessor methods to access the values of the record components.
Regarding the canonical constructor: it's indeed the case that for normal class
declarations the *default* constructor gets the access modifier from the
enclosing class declaration. But, this is a weak argument for *canonical*
constructors, which are different in a number of ways already.
I can see 4 options:
1. Keep the everything public option as currently spec-ed and implemented. This
is simple to remember, and not without precedent.
2. Change the current spec and implementation so the accessor methods and
canonical constructor inherit the accessibility from the record type. This is
also simple to remember, but it ignores the issues with interfaces detailed
above.
3. Incorporate both strategies: Perhaps that the canonical constructor inherits
accessibility from the record type, whereas accessor methods are required to be
public.
4. Some variant of (2) that can deal with the interface example (?)
What are your thoughts?
#2. Annotating explicit accessor methods
> Q10. Special annotation for explicit declaration of accessors.
>
> Tagir [6] proposes a new `@RecordAccessor` annotation to mark explicit
> accessors, much as `@Override` is used to mark method overrides.
>
> A: Rather than introduce a new accessor, we will consider extending the
> meaning of the `@Override` annotation to include this case.
There's been some discussion about this, but we were hoping for more! The
motivation for co-opting @Override is that its purpose already is to allow the
compiler to give us better type checking because we’ve captured a bit of user
intent, so this feels similar. However, Peter has made the excellent point that
extending `@Override` in this way might be confusing in record declarations that
also contain implementations of methods from interfaces.
Essentially, I think our options are:
1. Do nothing. (We can come back to this at the next release.)
2. Co-op `@Override`
3. Invent a new annotation, as suggested by Tagir.
Any further thoughts?
---
Thanks for your consideration. I look forward to hearing your feedback.
Best wishes,
Gavin
More information about the amber-spec-experts
mailing list