Java 14 records canonical constructor
Christian Beikov
christian.beikov at gmail.com
Thu Jun 4 18:09:17 UTC 2020
Why are the arguments mutable? IMO they shoul be final for the canonical
constructor. I can alter the example to violate the plain data carrier
property:
public record UserRecord(Integer id, String userName) {
public UserRecord {
if (Character.isUpperCase(userName.charAt(0))) {
userName = null;
} else {
userName = userName.toUpperCase();
}
}
}
I don't know how you are anticipating to implement de-construction
pattern matching, but if the reason for records being plain data
carriers is because you want to construct records to use them as
carriers for the pattern matching, then such a record would fail to be
usable with pattern matching, because the canonical constructor fails to
create the record based on the components.
Am 04.06.2020 um 20:01 schrieb Brian Goetz:
> tl;dr: you're right, and we've already fixed it for an upcoming preview.
>
> By way of explanation about how this came about ... it took us a
> little bit of time to come to the current formulation of "compact
> constructor", and the thinking about auto-committing fields predates
> the current formulation. So the behavior you are seeing has its roots
> as "more lenient constructors", rather than encouraging a different
> construction mode. It took a while for the details to catch up with
> the programming model on this one. Surely the intent was as you say
> -- the constructor should only contain stuff that can't be derived
> from the record declaration, which means it will often be empty.
>
> The model we have now is: the constructor arguments show up as set by
> the caller, but are mutable -- if you want to normalize them, do so by
> mutating them, and then they all get committed to the fields at once.
> The benefit here is that there are fewer weird orderings that the user
> has to reason about.
>
> This is a great example of why we do preview releases -- since its
> hard to imagine all of these up front.
>
> On 6/4/2020 1:53 PM, Christian Beikov wrote:
>> Hello,
>>
>> I recently found out that a record constructor can set the record
>> components explicitly and was kind of surprised. I thought that a
>> record is a plain data carrier and that I can re-construct a record
>> object from it's components, but this is not guaranteed.
>>
>> Here a quick example:
>>
>> public record UserRecord(Integer id, String userName) {
>> public UserRecord {
>> if (Character.isUpperCase(userName.charAt(0))) {
>> this.userName = null;
>> } else {
>> this.userName = userName.toUpperCase();
>> }
>> }
>> }
>>
>> IMO setting a field/record component directly should be prohibited. A
>> developer should move such a logic to a static method or a different
>> constructor. The canonical record constructor should only do sanity
>> checks i.e. argument is not-null, not blank etc. but not alter the
>> arguments or set the fields manually. It's ok for an additional
>> non-canonical constructor to alter the arguments though.
>>
>> What do you think? Was that an oversight or is this intended? If it
>> is intended, what is the reasoning for allowing this?
>>
>> I don't have a problem with this behavior. I just wouldn't have
>> expected it. If I remember correctly, there were some talks about
>> de-construction patterns at conferences that pointed out, that being
>> able to re-construct a record from the component state is a vital
>> property of records.
>>
>> Regards,
>>
>> Christian
>>
>
More information about the amber-dev
mailing list