Records: construction and validation

Brian Goetz brian.goetz at
Mon Mar 12 22:03:21 UTC 2018

 > Which leads to a question: if we have a record constructor with no 
explicit constructor call, do we do the default initialization at the 
beginning or the end?

Thinking some more, I think there's a better (but somewhat surprising) 

In all classes, the compiler inserts a super call if there is no 
explicit super/this, and it does so at the beginning.  This argues for 
putting the implicit initialization at the top, since we already do 
something similar elsewhere.  But ... this still gets in the way of 
normalizing fields of the current class.  So, let's refine this answer; 
if there is no explicit super/this, we put the implicit *super call* at 
the _top_ and the implicit *field initialization* _at the bottom_.  So given

     abstract record A(int a) { }
     record B(int a, int b) extends A(a) { }


     public B {
         if (b <= 0)
             throw new IllegalArgumentException("b");

is really shorthand for

     public B {
         super(a);  // implicit
         if (b <= 0)
             throw new IllegalArgumentException("b");
         this.b = b;  // implicit

This preserves consistency with implicit super in other cases, and also 
allows explicit code to normalize parameters without additional 
ceremony, as in:

     B {
         if (b < 0) b = 0;

Here, the assignment to the parameter b will happen before the implicit 
assignment of `this.b = b`, so we get what we expect.  And in:

     B {
         cachedSum = a + b;

the receiver is now DA at this point, so this is allowed too.  (If you 
happen to reference this.b, you'll be told this is DU, so you can't make 
a mistake there.)  So in almost no circumstances will you need an 
explicit default.this(...) call.

More information about the amber-spec-experts mailing list