New candidate JEP: 468: Derived Record Creation (Preview)

forax at univ-mlv.fr forax at univ-mlv.fr
Sat Apr 20 17:48:45 UTC 2024


> From: "attila kelemen85" <attila.kelemen85 at gmail.com>
> To: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "amber-dev" <amber-dev at openjdk.org>, "Gavin Bierman"
> <gavin.bierman at oracle.com>, "jdk-dev" <jdk-dev at openjdk.org>
> Sent: Saturday, April 20, 2024 7:35:34 PM
> Subject: Re: New candidate JEP: 468: Derived Record Creation (Preview)

> The compilation strategy doesn't matter. I'm just considering what the JEP
> implies (at least given my understanding) about the meaning of the code. What
> you are saying is relevant for binary compatibility (which I don't assume is
> broken). My problem is that when the example code is recompiled against the new
> version of `MyRecord`, then according to the JLS `MyProperty.z` shadows the
> `int z = 26;` (the JEP explicitly states this). So, the compiler must produce
> different code for the two variants of the record (otherwise it breaks the spec
> written in the JEP).

Okay, got it ! 

You are right, this is a serious issue, the semantics is different depending on if MyRecord has a field "z" or not. 

Rémi 

> Remi Forax < [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr ] > ezt írta
> (időpont: 2024. ápr. 20., Szo, 19:30):

>>> From: "attila kelemen85" < [ mailto:attila.kelemen85 at gmail.com |
>>> attila.kelemen85 at gmail.com ] >
>>> To: "amber-dev" < [ mailto:amber-dev at openjdk.org | amber-dev at openjdk.org ] >
>>> Cc: "Gavin Bierman" < [ mailto:gavin.bierman at oracle.com |
>>> gavin.bierman at oracle.com ] >, "jdk-dev" < [ mailto:jdk-dev at openjdk.org |
>>> jdk-dev at openjdk.org ] >
>>> Sent: Saturday, April 20, 2024 5:49:22 PM
>>> Subject: Re: New candidate JEP: 468: Derived Record Creation (Preview)

>>> I have a backward compatibility concern about this JEP. Consider that I have the
>>> following record:
>>> `record MyRecord(int x, int y) { }`

>>> One day I realize that I need that 3rd property which I want to add in a
>>> backward compatible way, which I will do the following way:

>>> ```
>>> record MyRecord(int x, int y, int z) {
>>> public MyRecord(int x, int y) {
>>> this(x, y, 0);
>>> }
>>> }
>>> ```

>>> As far as I understand, this will still remain binary compatible. However, if I
>>> didn't miss anything, then this JEP makes it non-source compatible, because
>>> someone might wrote the following code:

>>> ```
>>> var obj1 = new MyRecord(1, 2);
>>> int z = 26;
>>> var obj2 = obj1 with { y = z; }
>>> ```

>>> If this code is compiled again, then it will compile without error, but while in
>>> the first version `obj2.y == 26`, now `obj2.y == 0`. This seems rather nasty to
>>> me because I was once bitten by this in Gradle (I can't recall if it was Groovy
>>> or Kotlin, but it doesn't really matter), where this is a threat, and you have
>>> to be very careful adding a new property in plugin extensions with a too
>>> generic name. Even though Gradle scripts are far less prone to this, since
>>> those scripts are usually a lot less complicated than normal code.

>>> I saw in the JEP that on the left hand side of the assignment this issue can't
>>> happen, but as far as I can see the above problem is not prevented.

>>> My proposal would be to, instead of shadowing variables, raise a compile time
>>> error when the property name would shadow another variable. Though that still
>>> leaves the above example backward incompatible, but at least I would be
>>> notified of it by the compiler, instead of the compiler silently creating a
>>> bug.

>>> Another solution would be that the shadowing is done in the opposite order, and
>>> the `int z = 26;` shadows the record property (with a potential warning). In
>>> this case it would be even source compatible, if I didn't miss something.

>>> Attila

>> Hello, i think you are pre-supposing a specific compiler translation strategy,
>> but it can be compiled like this:

>> MyRecord obj1 = new MyRecord(1, 2);
>> int z = 26;

>> Object carrier = indy(r); // create a carrier object by calling all the
>> accessors
>> // so the side effects are done before calling the block
>> int y = z;
>> MyRecord obj2 = indy(carrier, /*y:*/ y); // create an instance of MyRecord using
>> 'y' and the values in the carrier

>> With the carrier instances working like [
>> https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/lang/runtime/Carriers.java
>> |
>> https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/lang/runtime/Carriers.java
>> ]

>> regards,
>> Rémi

>>> Mark Reinhold < [ mailto:mark.reinhold at oracle.com | mark.reinhold at oracle.com ] >
>>> ezt írta (időpont: 2024. febr. 28., Sze, 21:04):

>>>> [ https://openjdk.org/jeps/468 | https://openjdk.org/jeps/468 ]

>>>> Summary: Enhance the Java language with derived creation for
>>>> records. Records are immutable objects, so developers frequently create
>>>> new records from old records to model new data. Derived creation
>>>> streamlines code by deriving a new record from an existing record,
>>>> specifying only the components that are different. This is a preview
>>>> language feature.

>>>> - Mark
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/jdk-dev/attachments/20240420/f8cf8ca3/attachment.htm>


More information about the jdk-dev mailing list