instance initializer
Maurizio Cimadamore
maurizio.cimadamore at oracle.com
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:
this(...)
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
Maurizio
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