Record construction

Brian Goetz brian.goetz at
Wed Mar 14 14:55:54 UTC 2018

Let me summarize what we're proposing for record constructors.  I think 
we can simplify it so that it is easy to do parameter validation and 
normalization without falling off the syntactic cliff, and with fewer 
new features.  (As a bonus, I think these ideas mostly generalize to 
more concise non-record constructors too, but I'm going to leave that 
for another day -- but I'll just note that one of our goals for records 
is for them to be "just macros" for a set of finer-grained features, so 
that even if a class doesn't meet the requirements to be a record, it 
can still benefit from more concise construction, or equals/hashCode, or 


it should be easy to add constructor parameter validation, like

     Preconditions.require(x > 0);

without significant repetition of record elements.  Similarly, it should 
be easy to normalize arguments before they are committed to fields:

     if (name == null)
         name = "";

again without significant repetition.  If there are ancillary fields, it 
should be easy to initialize them in the constructor body without 
bringing back any boilerplate.

Ideally, these mechanisms are consistent with construction idioms for 
non-record classes (records and classes should not be semantically 

Record Constructors

A record class:

     record Point(int x, int y) { }

has a "class signature" `(int x, int y)`.  Record classes always have a 
_default constructor_ whose signature matches the class signature; these 
can be implicit or explicit.

The constructor syntax

     record Point(int x, int y) {
         Point {

is proposed as a shorthand for an explicit default constructor:

     record Point(int x, int y) {
         Point(int x, int y) {

The arguments A0..An are divided into super-arguments A0..Ak and 
this-arguments Ak+1..An.  The default super invocation is super(A0..Ak).

If a default constructor does not contain an explicit super-call, an 
implicit super constructor call is provided at the start of the 
constructor, just as we do now with implicit no-arg super-constructors.

An implicit field initialization, consisting of `this.xi = xi` for i in 
k+1..n, is added to the _end_ of the constructor.

This makes both validation and normalization work; if the constructor 
body contains only:

     Preconditions.require(x > 0);

(this is just a library call), the code is executed after the 
super-call, and `x` refers to the arguments.  Just like in constructors 
today.  Similarly, normalization:

     if (name == null)
         name = "";

is executed after the super-call, but before the field initialization, 
so any the update to the parameter "sticks", and `name` refers to the 
argument, not the field, just as today.  So existing idioms work, just 
by removing the boilerplate.

We can protect against double-initialization either by (a) not allowing 
the user to explicitly initialize fields corresponding to Ak+1..An, or 
(b) if the corresponding field is definitely initialized by the explicit 
constructor body, leave it out of the implicit initialization code.

If users want to adjust what gets passed to the super constructor, just 
use an explicit super-call.  If users want to adjust what gets written 
to the field, overwrite the parameter before it is written to the 
field.  Both of these are consistent with how constructors work today.

If the record has extra fields (if allowed), the constructor can just 
initialize them:

     Point {
         norm = Math.sqrt(x*x + y*y);

Again, `x` and `y` here refer to constructor arguments, and everything 
works, with no repetition.

  - The required idioms work, just leaving out the boilerplate
  - Very similar to existing constructors
  - No need to support statements before super (though we can add this 
later, if we see fit)
  - No need for `default.this(...)` idiom *at all*

(pause for agreement)

More information about the amber-spec-observers mailing list