> 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) answer:

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) { }

then

    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.


Reply via email to