Fwd: JDK 14 Preview Records constructors

Brian Goetz brian.goetz at oracle.com
Tue Jun 9 19:24:42 UTC 2020


Received on the spec-comments list.




-------- Forwarded Message --------
Subject: 	JDK 14 Preview Records constructors
Date: 	Tue, 9 Jun 2020 00:00:09 +0200
From: 	interlink.sg7 at gmail.com
To: 	amber-spec-comments at openjdk.java.net
CC: 	james.laskey at oracle.com



Hello,


I'm not sure if this is the correct place to give feedback on Records, I
apologise if it isn't.

I recently played around with the preview Records features from JDK 14 a bit
(mainly JShell).

There's a few things I'm at odds with and I'd like to highlight only those
in this post and I hope you can give me some insights that led to certain
restrictions

and where I'm misunderstanding some things maybe.

My main issue is mostly how different constructors work in Records compared
to Classes and the resulting inconsistency.

The following examples are a bit constructed and don't make logical sense,
but I hope my idea gets across :)


1. Canonical constructor can not call <custom constructor>, e.g.
delegating to spezialized normalizing constructor with a constant default
value:


record Example1(int x) {

public Example1(int x) {

this(x, 0); // compile error

}


public Example1(int x, int defaultX) {

this.x = x >= 0 ? x : Math.max(defaultX, 0);

}

}


2. <Custom constructors> must call canonical constructor, e.g. support
multiple input types or when you can't reuse canonical constructor:


record Example2(UUID uuid) {

public Example2 {

uuid = UUID.nameUUIDFromBytes(uuid.toString().getBytes());

}


public Example2() { // compile error

this.uuid = UUID.randomUUID();

}


public Example2(String uuid) { // compile error

this.uuid = UUID.fromString(uuid);

}

}



3. Canonical Constructor must be public, so it's not possible to have
only static factories or it forces normalization outside of Records
constructor:


record Example3(UUID uuid) {


private Example3 { // compile error

}


public Example3(String uuid) { // compile error

String uuidNormalized = StringUtils.toLowerCase(uuid);

try {

this.uuid = UUID.fromString(uuidNormalized);

catch(IllegalArgumentException e) {

this.uuid = null;

}

}


public static Example3 fromString(String uuid) {

return new Example3(uuid);

}

}


4. Assignment without <this> in canonical constructor is very
unnatural, e.g. use of <this> in Classes is very logical when you would
otherwise reassign the parameter

In Records however :


record Example4(int x) {

public Example4 {

x = x + 1; // assigns field, but looks nothing <like Java>

}


public Example4(int x, int plus) {

this(x); // hrrng

x = x + plus; // reassign parameter

}

}


record Example5(int x) {

public Example5 {

this.x = x + 1; // read it might be forbidden in the future :(

}


public Example5(int x, int plus) {

this(x); // hrrng

this.x = x + plus; // is compile error

}

}


5. It's not possible to define the canonical constructor with the exact
same parameter list as the Record definition has.

The simple canonical constructor without parameters should just be an alias
for that in my opinion.

For symmetrie reasons (with other constructors) and clearity I would like to
define the canonical constructor with the required parameters.


I think most of those discrepancies (especially 4) and 5)) come from the
desire to be less verbose and the definition of records (state and only
state) and still have some <convenience> like Classes.

However the limitations and half-way implicit <magic> makes it really
confusing. I think it would have been better to keep constructors similar to
the ones from Classes if explicitly defined.

I mean if the user is already going to manually write constructors (which
should be a special case) the few lines of assignments don't matter compared
to the rethinking it needs each time

with implicits and the restrictions that follow.


What are your thoughts?


Kind regards,


Simon






More information about the amber-dev mailing list