instance initializer

Maurizio Cimadamore maurizio.cimadamore at
Tue Oct 15 16:10:47 UTC 2019

Hi Peter,
we've been thinking more about this... as it was noted in this thread, 
perhaps the biggest value of 'lone' instance initializers is that they 
contain _common_ initialization code that is replicated across _all_ 
constructors. Do we need this for records? Probably no, we don't - the 
reason being that a record is a well-behaved construct that acts as a 
transparent wrapper for its state, with an equally well-defined way to 
construct it (the canonical constructor).

The way I see it, is that it is more a bug than a feature the fact that 
you can have 5 unrelated constructors on a record class - because one of 
the values behind opting for a record (vs. a class) in the first place, 
is to have control over the way in which record instances are created - 
which is in fact backed up by serialization as well (serialization 
guarantees that creation will always go through the canonical constructor).

So I think having a constructor that doesn't delegate to the canonical 
constructor is potentially dangerous, as it exposes an alternate way to 
construct a record which is not the _blessed_ one, might skip some of 
the invariant checks etc.

Let's say we demand that all secondary constructors starts off by 
delegating to the canonical constructor:


If that's the case, then all initialization goes through the canonical 
constructor, which also means that the canonical constructor is also the 
code where you'd put the statements you otherwise would have put into an 
instance initializer.

In other words, we want alternate constructors to be used as a way of 
providing convenience overloads, as in this case:

record Point(int x, int y) {

     Point() { this(0, 0); }


I don't think there's anything to be gain by making secondary 
constructors more flexible as, by doing so, we would also lose an 
important invariant of record construction - that all record creation 
flows through the canonical constructor.

Therefore, I'd like to propose that:

* a secondary, non-canonical constructor must always start with 
this(...) (the target of the delegation might be another secondary 
constructor, but you will eventually reach the canonical one)
* instance initializers on records are banned


On 06/10/2019 17:51, Peter Levart wrote:
> On Sunday, October 6, 2019 6:21:45 PM CEST Brian Goetz wrote:
>>> In these scheme, canonical constructor also acts as instance initializer,
>>> since it is always called from other constructors. Classical instance
>>> initializer is therefore not needed any more and could be prohibited in
>>> record types.
>> I would agree that instance initializers in records are mostly useless,
>> and keeping them around adds some complexity.  Any work that can be done
>> in an II could also be done in a compact ctor with about the same number
>> of keystrokes:
>>       { ++instanceCount; }
>> vs
>>      Foo { ++instanceCount; }
>> The argument for keeping them is to minimize the number of gratuitous
>> differences between records and classes.  But, "it is a compile-time
>> error for a record class to have an instance initializer" is a pretty
>> simple spec change... and probably no one will notice.
> I know that making special rules in record constructors (about being able to
> access instance fields) is also increasing the number of gratuitous
> differences between records and classes, but in order to ban instance
> initializers, there has to be a an alternative way to specify initializing
> code that is always executed. Perhaps it is enough just to suggest users to
> put such code into canonical constructor and always call that constructor from
> other constructors as opposed to forcing them to do that with language
> constraints.
> Regards, Peter

More information about the amber-spec-experts mailing list