From scolebourne at joda.org Mon Dec 1 02:30:23 2025 From: scolebourne at joda.org (Stephen Colebourne) Date: Mon, 1 Dec 2025 02:30:23 +0000 Subject: Reflection on records In-Reply-To: References: Message-ID: You'll need to write the annotation processor yourself I'm afraid, this is a design outline. Stephen On Sun, 30 Nov 2025, 21:19 David Alayachew, wrote: > Thanks Stephen. > > Hmmmmm, so this is definitely the best solution I have seen thus far. The > only real downside is that the static final field has a different name than > the actual record component. But I can probably fix that by making it an > inner class or something, as opposed to sharing the same namespace as the > record itself. > > Can you link me to the annotation code? > > On Sun, Nov 30, 2025 at 4:05?PM Stephen Colebourne > wrote: > >> There is a way to do this, but it isn't pretty. Use annotation processing. >> >> @GeneratedRecordComponentInterface >> record User(String firstName, String lastName, ComplexObject >> complexColumn) implements UserColumns {} >> >> // annotation processor generates: >> public interface UserColumns { >> public static final RecordComponentWrapper >> FIRST_NAME_COMPONENT = new RecordComponentWrapper(User.class, >> "firstName"); >> public static final RecordComponentWrapper >> LAST_NAME_COMPONENT = new RecordComponentWrapper(User.class, >> "lastName"); >> public static final RecordComponentWrapper >> COMPLEX_COLUMN_COMPONENT = new RecordComponentWrapper(User.class, >> "complexColumn"); >> } >> >> where `RecordComponentWrapper` is some suitable type-safe wrapper for >> record components. >> >> Although there is a string for each record component, it is in >> generated code, thus won't get out of sync. >> >> (This is the annotation processing equivalent to how Joda-Beans >> meta-properties have worked for many years) >> Stephen >> >> On Sun, 30 Nov 2025 at 20:39, David Alayachew >> wrote: >> > >> > Thanks everyone for the context. The answers I am getting are very >> helpful. I do see a comment about XY problem, so here is the full context >> of my problem. >> > >> > Unlike normal classes, record are transparent data carriers. Which make >> them useful for simple DTO's and serialization. Specifically, I wanted to >> create a De/serialization format for a CSV file. >> > >> > Let's say I have record User(String firstName, String lastName, >> ComplexObject complexColumn) {}, and each RecordComponent corrsponds to a >> column of my CSV, while each instance of User corresponds to a single row. >> Let's also assume that firstName and lastName are computationally cheap to >> deserialize, but complexColumn is expensive (complex but necessary regex). >> > >> > Well, I want to create a CSV De/Serialization tool that allows me to >> not only deserialize the object in full, but also deserialize only the >> specific component I want. In this case, if all I care about is firstName >> and lastName, then why should I pay the price of also deserializing >> complexColumn, which I won't use at all? >> > >> > Hence why I requested these various things in the thread -- such as the >> ability to reference RecordComponents, or why I wanted to use a method >> reference to a records accessors. I wanted some way to get compile time >> type safety about the column I am requesting. If I change my User data type >> such that firstName now says first instead, I want all callers of my tool >> to get compile time errors. But I don't see how to do it. >> > >> > I really do think that, if records claim to be transparent carriers of >> data, then being able to isolate and point to a single RecordComponent in a >> type safe way that will fail at compile time if I am out of sorts sounds >> like a reasonable thing to want from a record. >> > >> > >> > On Sun, Nov 30, 2025 at 3:04?PM Kirill Semyonkin >> wrote: >> >> >> >> Hello David, >> >> >> >> I've seen some people (ab)use Serializable and SerializedLambda to get >> names of the class and the method, with numerous examples like >> https://stackoverflow.com/a/53397378/16208077. From there you can >> traverse usual reflection with those strings to get the exact >> RecordComponent or some other reflection object. But those strings come >> from users doing method references, so you will get the behavior that you >> wanted, at least to some extent. Although it slightly feels like an XY >> problem. >> >> >> >> - Kirill Semyonkin >> >> >> >> ??, 30 ????. 2025??. ? 22:57, Attila Kelemen < >> attila.kelemen85 at gmail.com>: >> >>> >> >>> I guess, if your record type is fixed, then you can have a dummy >> instance of that record with all fields being different (hopefully, no 3 >> boolean components :)), and then you can do the same hack as with the >> interface + proxy combo. >> >>> >> >>> Attila Kelemen ezt ?rta (id?pont: 2025. >> nov. 30., V, 20:51): >> >>>> >> >>>> I think that is impossible. What I did when I needed something >> similar is that I used an interface instead of a record and built utilities >> around it, because in that case you can imitate what you want. >> >>>> >> >>>> That is, consider that you have `interface User { String >> firstName(); String lastName() }`. In this case, you could have methods >> taking a `Function` and then you can create an instance of `User` >> via `Proxy.newProxyInstance` in which you record which method was called, >> and pass the proxy instance to the `Function`. Assuming that someone passed >> `User::firstName` for the `Function`, you can detect which method was >> passed. The drawbacks are obvious, but it is better than passing strings in >> my opinion. >> >>>> >> >>>> David Alayachew ezt ?rta (id?pont: 2025. >> nov. 30., V, 20:41): >> >>>>> >> >>>>> Thanks for the response Attila. >> >>>>> >> >>>>> Let me try and loosen the constraints then -- is there any way for >> me to get a RecordComponent corresponding to firstName without needing to >> do String comparison? >> >>>>> >> >>>>> At the end of the day, that's all that I really need. >> >>>>> >> >>>>> As is now, the only way I can see to get a RecordComponent is to >> drill down from j.l.Class --> j.l.r.RecordComponent, then do String >> comparison against a provided String to get the record component that I >> want. That is unsafe and stringly typed, so, very much undesirable. After >> all, if I change my record to say first instead of firstName, I want a >> compiler error. But doing it that way, I won't -- I'll get a runtime error. >> >>>>> >> >>>>> So that's what I really want -- a type-safe way to isolate a record >> component from a record, without forcing my users to have to provide a >> String corresponding to the record component name. >> >>>>> >> >>>>> On Sun, Nov 30, 2025 at 2:16?PM Attila Kelemen < >> attila.kelemen85 at gmail.com> wrote: >> >>>>>> >> >>>>>> I'm pretty sure there is no such thing, because that essentially >> implies the existence of some kind of method literal (well, it would not be >> strictly necessary, but the JLS would feel strange without it), and there >> is no such thing (though it would be awesome, if there was). >> >>>>>> >> >>>>>> Also, note that if this was a thing, then your case is just a very >> special case, and you would want more (I would for sure). That is, in that >> case, I would also want type safety. So, something like >> MethodReference<(MyRecord) -> String> (which of course would require >> function types in Java). >> >>>>>> >> >>>>>> When I needed this, luckily I could restrain my need to interface >> methods (as opposed to your record getters) where I could create a `Proxy` >> and see which method gets called (nasty hack, has its downsides, but felt >> like the safest to me). >> >>>>>> >> >>>>>> Attila >> >>>>>> >> >>>>>> David Alayachew ezt ?rta (id?pont: >> 2025. nov. 29., Szo, 20:50): >> >>>>>>> >> >>>>>>> And by all means, add more parameters to foo if you want. For >> example, if a User.class helps, please do so! >> >>>>>>> >> >>>>>>> I just don't want to do anything like this. >> >>>>>>> >> >>>>>>> foo("firstName"); >> >>>>>>> >> >>>>>>> That's stringly typed, and undesirable for my use case. >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Mon Dec 1 09:55:31 2025 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Mon, 1 Dec 2025 10:55:31 +0100 Subject: Reflection on records In-Reply-To: References: Message-ID: There are some other downsides as well: You will have to force your clients to specifically design their records to your API (i.e., annotate it, and configure the annotation processor). Though, in your case that is probably not too bad, because likely they want to design their DTO for your API anyway. The other downside is that you will have to write the IDE support as well (e.g., Idea's editor will have red curlys around without it). Which, aside from the additional maintenance cost, can be a problem for a user who is not allowed to install arbitrary IDE plugins. Also, compared to the original outline, you probably want an additional generic argument for the record type on `RecordComponentWrapper` for type safety. David Alayachew ezt ?rta (id?pont: 2025. dec. 1., H, 1:53): > Thanks Stephen. > > Hmmmmm, so this is definitely the best solution I have seen thus far. The > only real downside is that the static final field has a different name than > the actual record component. But I can probably fix that by making it an > inner class or something, as opposed to sharing the same namespace as the > record itself. > > Can you link me to the annotation code? > > On Sun, Nov 30, 2025 at 4:05?PM Stephen Colebourne > wrote: > >> There is a way to do this, but it isn't pretty. Use annotation processing. >> >> @GeneratedRecordComponentInterface >> record User(String firstName, String lastName, ComplexObject >> complexColumn) implements UserColumns {} >> >> // annotation processor generates: >> public interface UserColumns { >> public static final RecordComponentWrapper >> FIRST_NAME_COMPONENT = new RecordComponentWrapper(User.class, >> "firstName"); >> public static final RecordComponentWrapper >> LAST_NAME_COMPONENT = new RecordComponentWrapper(User.class, >> "lastName"); >> public static final RecordComponentWrapper >> COMPLEX_COLUMN_COMPONENT = new RecordComponentWrapper(User.class, >> "complexColumn"); >> } >> >> where `RecordComponentWrapper` is some suitable type-safe wrapper for >> record components. >> >> Although there is a string for each record component, it is in >> generated code, thus won't get out of sync. >> >> (This is the annotation processing equivalent to how Joda-Beans >> meta-properties have worked for many years) >> Stephen >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Mon Dec 1 10:22:31 2025 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 1 Dec 2025 11:22:31 +0100 Subject: Reflection on records In-Reply-To: References: Message-ID: I think this will surely be possible for RecordComponents with code reflection (project Babylon), using the syntax you propose. You can create a quotable function like this: import jdk.incubator.code.*; @Reflect @FunctionalInterface interface Accessor { Object get(Record r); } Then create something like (just a sketch, sorry, I have no time now to provide a full-fledged sample): static RecordComponent foo(Accessor acc) { Quoted q = Op.ofQuotable(acc).orElseThrow(); // extract a method handle from q using Babylon API // unreflect it and find the corresponding RecordComponent } With best regards, Tagir Valeev On Sat, Nov 29, 2025 at 8:45?PM David Alayachew wrote: > Hello @amber-dev , > > I asked this on core-libs-dev already, but I figured I should ask here too. > > Let's say I have some record User(String firstName, String lastName) {}. > > Is there any possible way for me to do the following? > > java.lang.reflect.RecordComponent firstName = foo(User::firstName); > > I'll even accept this. > > java.lang.reflect.Method lastName = foo(User::lastName); > > Thank you for your time and consideration. > David Alayachew > -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Dec 1 13:33:05 2025 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 1 Dec 2025 08:33:05 -0500 Subject: Reflection on records In-Reply-To: References: Message-ID: Understood. Thanks @Stephen Colebourne . And yes @Attila Kelemen , I agree that it being an annotation does make it less ideal. Plus, I lose out on other things that the compiler might one day give us, like exhaustiveness checking. All in all, it is still the best solution, but not one I am a fan of. On Mon, Dec 1, 2025, 4:55?AM Attila Kelemen wrote: > There are some other downsides as well: You will have to force your > clients to specifically design their records to your API (i.e., annotate > it, and configure the annotation processor). Though, in your case that is > probably not too bad, because likely they want to design their DTO for your > API anyway. The other downside is that you will have to write the IDE > support as well (e.g., Idea's editor will have red curlys around without > it). Which, aside from the additional maintenance cost, can be a problem > for a user who is not allowed to install arbitrary IDE plugins. Also, > compared to the original outline, you probably want an additional generic > argument for the record type on `RecordComponentWrapper` for type safety. > > David Alayachew ezt ?rta (id?pont: 2025. dec. > 1., H, 1:53): > >> Thanks Stephen. >> >> Hmmmmm, so this is definitely the best solution I have seen thus far. The >> only real downside is that the static final field has a different name than >> the actual record component. But I can probably fix that by making it an >> inner class or something, as opposed to sharing the same namespace as the >> record itself. >> >> Can you link me to the annotation code? >> >> On Sun, Nov 30, 2025 at 4:05?PM Stephen Colebourne >> wrote: >> >>> There is a way to do this, but it isn't pretty. Use annotation >>> processing. >>> >>> @GeneratedRecordComponentInterface >>> record User(String firstName, String lastName, ComplexObject >>> complexColumn) implements UserColumns {} >>> >>> // annotation processor generates: >>> public interface UserColumns { >>> public static final RecordComponentWrapper >>> FIRST_NAME_COMPONENT = new RecordComponentWrapper(User.class, >>> "firstName"); >>> public static final RecordComponentWrapper >>> LAST_NAME_COMPONENT = new RecordComponentWrapper(User.class, >>> "lastName"); >>> public static final RecordComponentWrapper >>> COMPLEX_COLUMN_COMPONENT = new RecordComponentWrapper(User.class, >>> "complexColumn"); >>> } >>> >>> where `RecordComponentWrapper` is some suitable type-safe wrapper for >>> record components. >>> >>> Although there is a string for each record component, it is in >>> generated code, thus won't get out of sync. >>> >>> (This is the annotation processing equivalent to how Joda-Beans >>> meta-properties have worked for many years) >>> Stephen >>> >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Dec 1 13:35:15 2025 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 1 Dec 2025 08:35:15 -0500 Subject: Reflection on records In-Reply-To: References: Message-ID: Thanks @Tagir Valeev . I am still not great at the stuff Babylon is doing, so I can only roughly follow along. Regardless, it sounds like this is depending on Babylon features which have not yet landed in the mainline jdk, yes? On Mon, Dec 1, 2025, 5:22?AM Tagir Valeev wrote: > I think this will surely be possible for RecordComponents with code > reflection (project Babylon), using the syntax you propose. > > You can create a quotable function like this: > > import jdk.incubator.code.*; > > @Reflect > @FunctionalInterface > interface Accessor { > Object get(Record r); > } > > Then create something like (just a sketch, sorry, I have no time now to > provide a full-fledged sample): > > static RecordComponent foo(Accessor acc) { > Quoted q = Op.ofQuotable(acc).orElseThrow(); > // extract a method handle from q using Babylon API > // unreflect it and find the corresponding RecordComponent > } > > With best regards, > Tagir Valeev > > On Sat, Nov 29, 2025 at 8:45?PM David Alayachew > wrote: > >> Hello @amber-dev , >> >> I asked this on core-libs-dev already, but I figured I should ask here >> too. >> >> Let's say I have some record User(String firstName, String lastName) {}. >> >> Is there any possible way for me to do the following? >> >> java.lang.reflect.RecordComponent firstName = foo(User::firstName); >> >> I'll even accept this. >> >> java.lang.reflect.Method lastName = foo(User::lastName); >> >> Thank you for your time and consideration. >> David Alayachew >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Mon Dec 1 14:01:36 2025 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 1 Dec 2025 15:01:36 +0100 Subject: Reflection on records In-Reply-To: References: Message-ID: Yes, you have to build it from the Babylon repo. On Mon, Dec 1, 2025, 14:35 David Alayachew wrote: > Thanks @Tagir Valeev . > > I am still not great at the stuff Babylon is doing, so I can only roughly > follow along. Regardless, it sounds like this is depending on Babylon > features which have not yet landed in the mainline jdk, yes? > > > On Mon, Dec 1, 2025, 5:22?AM Tagir Valeev wrote: > >> I think this will surely be possible for RecordComponents with code >> reflection (project Babylon), using the syntax you propose. >> >> You can create a quotable function like this: >> >> import jdk.incubator.code.*; >> >> @Reflect >> @FunctionalInterface >> interface Accessor { >> Object get(Record r); >> } >> >> Then create something like (just a sketch, sorry, I have no time now to >> provide a full-fledged sample): >> >> static RecordComponent foo(Accessor acc) { >> Quoted q = Op.ofQuotable(acc).orElseThrow(); >> // extract a method handle from q using Babylon API >> // unreflect it and find the corresponding RecordComponent >> } >> >> With best regards, >> Tagir Valeev >> >> On Sat, Nov 29, 2025 at 8:45?PM David Alayachew >> wrote: >> >>> Hello @amber-dev , >>> >>> I asked this on core-libs-dev already, but I figured I should ask here >>> too. >>> >>> Let's say I have some record User(String firstName, String lastName) {}. >>> >>> Is there any possible way for me to do the following? >>> >>> java.lang.reflect.RecordComponent firstName = foo(User::firstName); >>> >>> I'll even accept this. >>> >>> java.lang.reflect.Method lastName = foo(User::lastName); >>> >>> Thank you for your time and consideration. >>> David Alayachew >>> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Dec 1 14:07:55 2025 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 1 Dec 2025 09:07:55 -0500 Subject: Reflection on records In-Reply-To: References: Message-ID: Understood. Thanks for clarifying. For now, I'll hold off on that, as I don't know how stable that is, and I don't really want to.build this new library off of it. Maybe when the respective JEP goes into preview. Could you link me to that JEP, or is there not one yet? On Mon, Dec 1, 2025, 9:01?AM Tagir Valeev wrote: > Yes, you have to build it from the Babylon repo. > > On Mon, Dec 1, 2025, 14:35 David Alayachew > wrote: > >> Thanks @Tagir Valeev . >> >> I am still not great at the stuff Babylon is doing, so I can only roughly >> follow along. Regardless, it sounds like this is depending on Babylon >> features which have not yet landed in the mainline jdk, yes? >> >> >> On Mon, Dec 1, 2025, 5:22?AM Tagir Valeev wrote: >> >>> I think this will surely be possible for RecordComponents with code >>> reflection (project Babylon), using the syntax you propose. >>> >>> You can create a quotable function like this: >>> >>> import jdk.incubator.code.*; >>> >>> @Reflect >>> @FunctionalInterface >>> interface Accessor { >>> Object get(Record r); >>> } >>> >>> Then create something like (just a sketch, sorry, I have no time now to >>> provide a full-fledged sample): >>> >>> static RecordComponent foo(Accessor acc) { >>> Quoted q = Op.ofQuotable(acc).orElseThrow(); >>> // extract a method handle from q using Babylon API >>> // unreflect it and find the corresponding RecordComponent >>> } >>> >>> With best regards, >>> Tagir Valeev >>> >>> On Sat, Nov 29, 2025 at 8:45?PM David Alayachew < >>> davidalayachew at gmail.com> wrote: >>> >>>> Hello @amber-dev , >>>> >>>> I asked this on core-libs-dev already, but I figured I should ask here >>>> too. >>>> >>>> Let's say I have some record User(String firstName, String lastName) {}. >>>> >>>> Is there any possible way for me to do the following? >>>> >>>> java.lang.reflect.RecordComponent firstName = foo(User::firstName); >>>> >>>> I'll even accept this. >>>> >>>> java.lang.reflect.Method lastName = foo(User::lastName); >>>> >>>> Thank you for your time and consideration. >>>> David Alayachew >>>> >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Mon Dec 1 14:09:59 2025 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 1 Dec 2025 15:09:59 +0100 Subject: Reflection on records In-Reply-To: References: Message-ID: I don't think there are any published JEPs. Here's some information about the project: https://openjdk.org/projects/babylon/ On Mon, Dec 1, 2025, 15:08 David Alayachew wrote: > Understood. Thanks for clarifying. For now, I'll hold off on that, as I > don't know how stable that is, and I don't really want to.build this new > library off of it. Maybe when the respective JEP goes into preview. Could > you link me to that JEP, or is there not one yet? > > On Mon, Dec 1, 2025, 9:01?AM Tagir Valeev wrote: > >> Yes, you have to build it from the Babylon repo. >> >> On Mon, Dec 1, 2025, 14:35 David Alayachew >> wrote: >> >>> Thanks @Tagir Valeev . >>> >>> I am still not great at the stuff Babylon is doing, so I can only >>> roughly follow along. Regardless, it sounds like this is depending on >>> Babylon features which have not yet landed in the mainline jdk, yes? >>> >>> >>> On Mon, Dec 1, 2025, 5:22?AM Tagir Valeev wrote: >>> >>>> I think this will surely be possible for RecordComponents with code >>>> reflection (project Babylon), using the syntax you propose. >>>> >>>> You can create a quotable function like this: >>>> >>>> import jdk.incubator.code.*; >>>> >>>> @Reflect >>>> @FunctionalInterface >>>> interface Accessor { >>>> Object get(Record r); >>>> } >>>> >>>> Then create something like (just a sketch, sorry, I have no time now to >>>> provide a full-fledged sample): >>>> >>>> static RecordComponent foo(Accessor acc) { >>>> Quoted q = Op.ofQuotable(acc).orElseThrow(); >>>> // extract a method handle from q using Babylon API >>>> // unreflect it and find the corresponding RecordComponent >>>> } >>>> >>>> With best regards, >>>> Tagir Valeev >>>> >>>> On Sat, Nov 29, 2025 at 8:45?PM David Alayachew < >>>> davidalayachew at gmail.com> wrote: >>>> >>>>> Hello @amber-dev , >>>>> >>>>> I asked this on core-libs-dev already, but I figured I should ask here >>>>> too. >>>>> >>>>> Let's say I have some record User(String firstName, String lastName) >>>>> {}. >>>>> >>>>> Is there any possible way for me to do the following? >>>>> >>>>> java.lang.reflect.RecordComponent firstName = foo(User::firstName); >>>>> >>>>> I'll even accept this. >>>>> >>>>> java.lang.reflect.Method lastName = foo(User::lastName); >>>>> >>>>> Thank you for your time and consideration. >>>>> David Alayachew >>>>> >>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Dec 1 14:15:33 2025 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 1 Dec 2025 09:15:33 -0500 Subject: Reflection on records In-Reply-To: References: Message-ID: ? David reacted via Gmail On Mon, Dec 1, 2025, 9:10?AM Tagir Valeev wrote: > I don't think there are any published JEPs. Here's some information about > the project: > https://openjdk.org/projects/babylon/ > > On Mon, Dec 1, 2025, 15:08 David Alayachew > wrote: > >> Understood. Thanks for clarifying. For now, I'll hold off on that, as I >> don't know how stable that is, and I don't really want to.build this new >> library off of it. Maybe when the respective JEP goes into preview. Could >> you link me to that JEP, or is there not one yet? >> >> On Mon, Dec 1, 2025, 9:01?AM Tagir Valeev wrote: >> >>> Yes, you have to build it from the Babylon repo. >>> >>> On Mon, Dec 1, 2025, 14:35 David Alayachew >>> wrote: >>> >>>> Thanks @Tagir Valeev . >>>> >>>> I am still not great at the stuff Babylon is doing, so I can only >>>> roughly follow along. Regardless, it sounds like this is depending on >>>> Babylon features which have not yet landed in the mainline jdk, yes? >>>> >>>> >>>> On Mon, Dec 1, 2025, 5:22?AM Tagir Valeev wrote: >>>> >>>>> I think this will surely be possible for RecordComponents with code >>>>> reflection (project Babylon), using the syntax you propose. >>>>> >>>>> You can create a quotable function like this: >>>>> >>>>> import jdk.incubator.code.*; >>>>> >>>>> @Reflect >>>>> @FunctionalInterface >>>>> interface Accessor { >>>>> Object get(Record r); >>>>> } >>>>> >>>>> Then create something like (just a sketch, sorry, I have no time now >>>>> to provide a full-fledged sample): >>>>> >>>>> static RecordComponent foo(Accessor acc) { >>>>> Quoted q = Op.ofQuotable(acc).orElseThrow(); >>>>> // extract a method handle from q using Babylon API >>>>> // unreflect it and find the corresponding RecordComponent >>>>> } >>>>> >>>>> With best regards, >>>>> Tagir Valeev >>>>> >>>>> On Sat, Nov 29, 2025 at 8:45?PM David Alayachew < >>>>> davidalayachew at gmail.com> wrote: >>>>> >>>>>> Hello @amber-dev , >>>>>> >>>>>> I asked this on core-libs-dev already, but I figured I should ask >>>>>> here too. >>>>>> >>>>>> Let's say I have some record User(String firstName, String lastName) >>>>>> {}. >>>>>> >>>>>> Is there any possible way for me to do the following? >>>>>> >>>>>> java.lang.reflect.RecordComponent firstName = foo(User::firstName); >>>>>> >>>>>> I'll even accept this. >>>>>> >>>>>> java.lang.reflect.Method lastName = foo(User::lastName); >>>>>> >>>>>> Thank you for your time and consideration. >>>>>> David Alayachew >>>>>> >>>>> -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: text/vnd.google.email-reaction+json Size: 37 bytes Desc: not available URL: -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Dec 1 14:16:02 2025 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 1 Dec 2025 09:16:02 -0500 Subject: Reflection on records In-Reply-To: References: Message-ID: Ty vm. Will look into it, and more. On Mon, Dec 1, 2025, 9:10?AM Tagir Valeev wrote: > I don't think there are any published JEPs. Here's some information about > the project: > https://openjdk.org/projects/babylon/ > > On Mon, Dec 1, 2025, 15:08 David Alayachew > wrote: > >> Understood. Thanks for clarifying. For now, I'll hold off on that, as I >> don't know how stable that is, and I don't really want to.build this new >> library off of it. Maybe when the respective JEP goes into preview. Could >> you link me to that JEP, or is there not one yet? >> >> On Mon, Dec 1, 2025, 9:01?AM Tagir Valeev wrote: >> >>> Yes, you have to build it from the Babylon repo. >>> >>> On Mon, Dec 1, 2025, 14:35 David Alayachew >>> wrote: >>> >>>> Thanks @Tagir Valeev . >>>> >>>> I am still not great at the stuff Babylon is doing, so I can only >>>> roughly follow along. Regardless, it sounds like this is depending on >>>> Babylon features which have not yet landed in the mainline jdk, yes? >>>> >>>> >>>> On Mon, Dec 1, 2025, 5:22?AM Tagir Valeev wrote: >>>> >>>>> I think this will surely be possible for RecordComponents with code >>>>> reflection (project Babylon), using the syntax you propose. >>>>> >>>>> You can create a quotable function like this: >>>>> >>>>> import jdk.incubator.code.*; >>>>> >>>>> @Reflect >>>>> @FunctionalInterface >>>>> interface Accessor { >>>>> Object get(Record r); >>>>> } >>>>> >>>>> Then create something like (just a sketch, sorry, I have no time now >>>>> to provide a full-fledged sample): >>>>> >>>>> static RecordComponent foo(Accessor acc) { >>>>> Quoted q = Op.ofQuotable(acc).orElseThrow(); >>>>> // extract a method handle from q using Babylon API >>>>> // unreflect it and find the corresponding RecordComponent >>>>> } >>>>> >>>>> With best regards, >>>>> Tagir Valeev >>>>> >>>>> On Sat, Nov 29, 2025 at 8:45?PM David Alayachew < >>>>> davidalayachew at gmail.com> wrote: >>>>> >>>>>> Hello @amber-dev , >>>>>> >>>>>> I asked this on core-libs-dev already, but I figured I should ask >>>>>> here too. >>>>>> >>>>>> Let's say I have some record User(String firstName, String lastName) >>>>>> {}. >>>>>> >>>>>> Is there any possible way for me to do the following? >>>>>> >>>>>> java.lang.reflect.RecordComponent firstName = foo(User::firstName); >>>>>> >>>>>> I'll even accept this. >>>>>> >>>>>> java.lang.reflect.Method lastName = foo(User::lastName); >>>>>> >>>>>> Thank you for your time and consideration. >>>>>> David Alayachew >>>>>> >>>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From artyomcool2 at gmail.com Mon Dec 1 14:57:33 2025 From: artyomcool2 at gmail.com (Artyom Drozdov) Date: Mon, 1 Dec 2025 15:57:33 +0100 Subject: Reflection on records In-Reply-To: References: Message-ID: Hello David, If you absolute sure that you need that, it is possible to extract method name and signature from method reference (and access via MethodHandle or Reflections later) by analyzing class file and InvokeDynamic instruction (you probably will need some time to learn how it works if you are not familiar yet). Personally, I'd prefer annotations + reflections + method handle approach (or drop the idea at all). I also think that resources like StackOverflow might suit your goal better than amber-dev mailing list) Regards, Artyom Drozdov ??, 1 ???. 2025??., 15:47 David Alayachew : > Understood. Thanks @Stephen Colebourne . > > And yes @Attila Kelemen , I agree that it > being an annotation does make it less ideal. Plus, I lose out on other > things that the compiler might one day give us, like exhaustiveness > checking. All in all, it is still the best solution, but not one I am a fan > of. > > > On Mon, Dec 1, 2025, 4:55?AM Attila Kelemen > wrote: > >> There are some other downsides as well: You will have to force your >> clients to specifically design their records to your API (i.e., annotate >> it, and configure the annotation processor). Though, in your case that is >> probably not too bad, because likely they want to design their DTO for your >> API anyway. The other downside is that you will have to write the IDE >> support as well (e.g., Idea's editor will have red curlys around without >> it). Which, aside from the additional maintenance cost, can be a problem >> for a user who is not allowed to install arbitrary IDE plugins. Also, >> compared to the original outline, you probably want an additional generic >> argument for the record type on `RecordComponentWrapper` for type safety. >> >> David Alayachew ezt ?rta (id?pont: 2025. dec. >> 1., H, 1:53): >> >>> Thanks Stephen. >>> >>> Hmmmmm, so this is definitely the best solution I have seen thus far. >>> The only real downside is that the static final field has a different name >>> than the actual record component. But I can probably fix that by making it >>> an inner class or something, as opposed to sharing the same namespace as >>> the record itself. >>> >>> Can you link me to the annotation code? >>> >>> On Sun, Nov 30, 2025 at 4:05?PM Stephen Colebourne >>> wrote: >>> >>>> There is a way to do this, but it isn't pretty. Use annotation >>>> processing. >>>> >>>> @GeneratedRecordComponentInterface >>>> record User(String firstName, String lastName, ComplexObject >>>> complexColumn) implements UserColumns {} >>>> >>>> // annotation processor generates: >>>> public interface UserColumns { >>>> public static final RecordComponentWrapper >>>> FIRST_NAME_COMPONENT = new RecordComponentWrapper(User.class, >>>> "firstName"); >>>> public static final RecordComponentWrapper >>>> LAST_NAME_COMPONENT = new RecordComponentWrapper(User.class, >>>> "lastName"); >>>> public static final RecordComponentWrapper >>>> COMPLEX_COLUMN_COMPONENT = new RecordComponentWrapper(User.class, >>>> "complexColumn"); >>>> } >>>> >>>> where `RecordComponentWrapper` is some suitable type-safe wrapper for >>>> record components. >>>> >>>> Although there is a string for each record component, it is in >>>> generated code, thus won't get out of sync. >>>> >>>> (This is the annotation processing equivalent to how Joda-Beans >>>> meta-properties have worked for many years) >>>> Stephen >>>> >>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Dec 1 16:09:31 2025 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 1 Dec 2025 11:09:31 -0500 Subject: Reflection on records In-Reply-To: References: Message-ID: Thank you Artyom. This mailing list is actually my final stop. I already asked in multiple other places. And thanks for the suggestions about MethodHandles and InvokeDynamic. I think that might be overkill, so I'll probably just abandon the idea for now. Either way, I sent a new message on the mailing list, asking for a new feature for records. All of the suggestions I've received thus far feel like trying to fit 10 pounds of dirt in a 5 pound bag. It feels like the language/compiler could step in and provide us with better tools. On Mon, Dec 1, 2025, 9:57?AM Artyom Drozdov wrote: > Hello David, > > If you absolute sure that you need that, it is possible to extract method > name and signature from method reference (and access via MethodHandle or > Reflections later) by analyzing class file and InvokeDynamic instruction > (you probably will need some time to learn how it works if you are not > familiar yet). > > Personally, I'd prefer annotations + reflections + method handle approach > (or drop the idea at all). > > I also think that resources like StackOverflow might suit your goal better > than amber-dev mailing list) > > Regards, > Artyom Drozdov > > ??, 1 ???. 2025??., 15:47 David Alayachew : > >> Understood. Thanks @Stephen Colebourne . >> >> And yes @Attila Kelemen , I agree that it >> being an annotation does make it less ideal. Plus, I lose out on other >> things that the compiler might one day give us, like exhaustiveness >> checking. All in all, it is still the best solution, but not one I am a fan >> of. >> >> >> On Mon, Dec 1, 2025, 4:55?AM Attila Kelemen >> wrote: >> >>> There are some other downsides as well: You will have to force your >>> clients to specifically design their records to your API (i.e., annotate >>> it, and configure the annotation processor). Though, in your case that is >>> probably not too bad, because likely they want to design their DTO for your >>> API anyway. The other downside is that you will have to write the IDE >>> support as well (e.g., Idea's editor will have red curlys around without >>> it). Which, aside from the additional maintenance cost, can be a problem >>> for a user who is not allowed to install arbitrary IDE plugins. Also, >>> compared to the original outline, you probably want an additional generic >>> argument for the record type on `RecordComponentWrapper` for type safety. >>> >>> David Alayachew ezt ?rta (id?pont: 2025. >>> dec. 1., H, 1:53): >>> >>>> Thanks Stephen. >>>> >>>> Hmmmmm, so this is definitely the best solution I have seen thus far. >>>> The only real downside is that the static final field has a different name >>>> than the actual record component. But I can probably fix that by making it >>>> an inner class or something, as opposed to sharing the same namespace as >>>> the record itself. >>>> >>>> Can you link me to the annotation code? >>>> >>>> On Sun, Nov 30, 2025 at 4:05?PM Stephen Colebourne < >>>> scolebourne at joda.org> wrote: >>>> >>>>> There is a way to do this, but it isn't pretty. Use annotation >>>>> processing. >>>>> >>>>> @GeneratedRecordComponentInterface >>>>> record User(String firstName, String lastName, ComplexObject >>>>> complexColumn) implements UserColumns {} >>>>> >>>>> // annotation processor generates: >>>>> public interface UserColumns { >>>>> public static final RecordComponentWrapper >>>>> FIRST_NAME_COMPONENT = new RecordComponentWrapper(User.class, >>>>> "firstName"); >>>>> public static final RecordComponentWrapper >>>>> LAST_NAME_COMPONENT = new RecordComponentWrapper(User.class, >>>>> "lastName"); >>>>> public static final RecordComponentWrapper >>>>> COMPLEX_COLUMN_COMPONENT = new RecordComponentWrapper(User.class, >>>>> "complexColumn"); >>>>> } >>>>> >>>>> where `RecordComponentWrapper` is some suitable type-safe wrapper for >>>>> record components. >>>>> >>>>> Although there is a string for each record component, it is in >>>>> generated code, thus won't get out of sync. >>>>> >>>>> (This is the annotation processing equivalent to how Joda-Beans >>>>> meta-properties have worked for many years) >>>>> Stephen >>>>> >>>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From chen.l.liang at oracle.com Mon Dec 1 16:45:54 2025 From: chen.l.liang at oracle.com (Chen Liang) Date: Mon, 1 Dec 2025 16:45:54 +0000 Subject: Reflection on records In-Reply-To: References: Message-ID: Hi David, before we investigate solutions, what is the problem you want to resolve? If you want to obtain a Method on a record class, you can do recordClass.getMethod("variable") to find that accessor method. Note that the Java Virtual Machine does not validate a record, that it may recognize a class with the record attribute but has no corresponding accessor methods or canonical constructor as a record class. -Chen Confidential- Oracle Internal ________________________________ From: amber-dev on behalf of David Alayachew Sent: Monday, December 1, 2025 10:09 AM To: Artyom Drozdov Cc: amber-dev Subject: Re: Reflection on records Thank you Artyom. This mailing list is actually my final stop. I already asked in multiple other places. And thanks for the suggestions about MethodHandles and InvokeDynamic. I think that might be overkill, so I'll probably just abandon the idea for now. Either way, I sent a new message on the mailing list, asking for a new feature for records. All of the suggestions I've received thus far feel like trying to fit 10 pounds of dirt in a 5 pound bag. It feels like the language/compiler could step in and provide us with better tools. On Mon, Dec 1, 2025, 9:57?AM Artyom Drozdov > wrote: Hello David, If you absolute sure that you need that, it is possible to extract method name and signature from method reference (and access via MethodHandle or Reflections later) by analyzing class file and InvokeDynamic instruction (you probably will need some time to learn how it works if you are not familiar yet). Personally, I'd prefer annotations + reflections + method handle approach (or drop the idea at all). I also think that resources like StackOverflow might suit your goal better than amber-dev mailing list) Regards, Artyom Drozdov ??, 1 ???. 2025??., 15:47 David Alayachew >: Understood. Thanks @Stephen Colebourne. And yes @Attila Kelemen, I agree that it being an annotation does make it less ideal. Plus, I lose out on other things that the compiler might one day give us, like exhaustiveness checking. All in all, it is still the best solution, but not one I am a fan of. On Mon, Dec 1, 2025, 4:55?AM Attila Kelemen > wrote: There are some other downsides as well: You will have to force your clients to specifically design their records to your API (i.e., annotate it, and configure the annotation processor). Though, in your case that is probably not too bad, because likely they want to design their DTO for your API anyway. The other downside is that you will have to write the IDE support as well (e.g., Idea's editor will have red curlys around without it). Which, aside from the additional maintenance cost, can be a problem for a user who is not allowed to install arbitrary IDE plugins. Also, compared to the original outline, you probably want an additional generic argument for the record type on `RecordComponentWrapper` for type safety. David Alayachew > ezt ?rta (id?pont: 2025. dec. 1., H, 1:53): Thanks Stephen. Hmmmmm, so this is definitely the best solution I have seen thus far. The only real downside is that the static final field has a different name than the actual record component. But I can probably fix that by making it an inner class or something, as opposed to sharing the same namespace as the record itself. Can you link me to the annotation code? On Sun, Nov 30, 2025 at 4:05?PM Stephen Colebourne > wrote: There is a way to do this, but it isn't pretty. Use annotation processing. @GeneratedRecordComponentInterface record User(String firstName, String lastName, ComplexObject complexColumn) implements UserColumns {} // annotation processor generates: public interface UserColumns { public static final RecordComponentWrapper FIRST_NAME_COMPONENT = new RecordComponentWrapper(User.class, "firstName"); public static final RecordComponentWrapper LAST_NAME_COMPONENT = new RecordComponentWrapper(User.class, "lastName"); public static final RecordComponentWrapper COMPLEX_COLUMN_COMPONENT = new RecordComponentWrapper(User.class, "complexColumn"); } where `RecordComponentWrapper` is some suitable type-safe wrapper for record components. Although there is a string for each record component, it is in generated code, thus won't get out of sync. (This is the annotation processing equivalent to how Joda-Beans meta-properties have worked for many years) Stephen -------------- next part -------------- An HTML attachment was scrubbed... URL: From ethan at mccue.dev Mon Dec 1 16:53:42 2025 From: ethan at mccue.dev (Ethan McCue) Date: Mon, 1 Dec 2025 11:53:42 -0500 Subject: Reflection on records In-Reply-To: References: Message-ID: Here is a thread from ~ a year ago which hit some similar notes https://mail.openjdk.org/pipermail/amber-dev/2024-December/009121.html On Mon, Dec 1, 2025 at 11:51?AM David Alayachew wrote: > Thank you Artyom. > > This mailing list is actually my final stop. I already asked in multiple > other places. > > And thanks for the suggestions about MethodHandles and InvokeDynamic. I > think that might be overkill, so I'll probably just abandon the idea for > now. > > Either way, I sent a new message on the mailing list, asking for a new > feature for records. All of the suggestions I've received thus far feel > like trying to fit 10 pounds of dirt in a 5 pound bag. It feels like the > language/compiler could step in and provide us with better tools. > > > On Mon, Dec 1, 2025, 9:57?AM Artyom Drozdov wrote: > >> Hello David, >> >> If you absolute sure that you need that, it is possible to extract method >> name and signature from method reference (and access via MethodHandle or >> Reflections later) by analyzing class file and InvokeDynamic instruction >> (you probably will need some time to learn how it works if you are not >> familiar yet). >> >> Personally, I'd prefer annotations + reflections + method handle approach >> (or drop the idea at all). >> >> I also think that resources like StackOverflow might suit your goal >> better than amber-dev mailing list) >> >> Regards, >> Artyom Drozdov >> >> ??, 1 ???. 2025??., 15:47 David Alayachew : >> >>> Understood. Thanks @Stephen Colebourne . >>> >>> And yes @Attila Kelemen , I agree that it >>> being an annotation does make it less ideal. Plus, I lose out on other >>> things that the compiler might one day give us, like exhaustiveness >>> checking. All in all, it is still the best solution, but not one I am a fan >>> of. >>> >>> >>> On Mon, Dec 1, 2025, 4:55?AM Attila Kelemen >>> wrote: >>> >>>> There are some other downsides as well: You will have to force your >>>> clients to specifically design their records to your API (i.e., annotate >>>> it, and configure the annotation processor). Though, in your case that is >>>> probably not too bad, because likely they want to design their DTO for your >>>> API anyway. The other downside is that you will have to write the IDE >>>> support as well (e.g., Idea's editor will have red curlys around without >>>> it). Which, aside from the additional maintenance cost, can be a problem >>>> for a user who is not allowed to install arbitrary IDE plugins. Also, >>>> compared to the original outline, you probably want an additional generic >>>> argument for the record type on `RecordComponentWrapper` for type safety. >>>> >>>> David Alayachew ezt ?rta (id?pont: 2025. >>>> dec. 1., H, 1:53): >>>> >>>>> Thanks Stephen. >>>>> >>>>> Hmmmmm, so this is definitely the best solution I have seen thus far. >>>>> The only real downside is that the static final field has a different name >>>>> than the actual record component. But I can probably fix that by making it >>>>> an inner class or something, as opposed to sharing the same namespace as >>>>> the record itself. >>>>> >>>>> Can you link me to the annotation code? >>>>> >>>>> On Sun, Nov 30, 2025 at 4:05?PM Stephen Colebourne < >>>>> scolebourne at joda.org> wrote: >>>>> >>>>>> There is a way to do this, but it isn't pretty. Use annotation >>>>>> processing. >>>>>> >>>>>> @GeneratedRecordComponentInterface >>>>>> record User(String firstName, String lastName, ComplexObject >>>>>> complexColumn) implements UserColumns {} >>>>>> >>>>>> // annotation processor generates: >>>>>> public interface UserColumns { >>>>>> public static final RecordComponentWrapper >>>>>> FIRST_NAME_COMPONENT = new RecordComponentWrapper(User.class, >>>>>> "firstName"); >>>>>> public static final RecordComponentWrapper >>>>>> LAST_NAME_COMPONENT = new RecordComponentWrapper(User.class, >>>>>> "lastName"); >>>>>> public static final RecordComponentWrapper >>>>>> COMPLEX_COLUMN_COMPONENT = new RecordComponentWrapper(User.class, >>>>>> "complexColumn"); >>>>>> } >>>>>> >>>>>> where `RecordComponentWrapper` is some suitable type-safe wrapper for >>>>>> record components. >>>>>> >>>>>> Although there is a string for each record component, it is in >>>>>> generated code, thus won't get out of sync. >>>>>> >>>>>> (This is the annotation processing equivalent to how Joda-Beans >>>>>> meta-properties have worked for many years) >>>>>> Stephen >>>>>> >>>>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From archie.cobbs at gmail.com Mon Dec 1 17:13:48 2025 From: archie.cobbs at gmail.com (Archie Cobbs) Date: Mon, 1 Dec 2025 11:13:48 -0600 Subject: Reflection on records In-Reply-To: References: Message-ID: I'm guessing what underlies David's question is this thought: Why can't the compiler provide more compile-time checking of Field and Method reflection objects? After all, it does this for Class objects by providing us with Class literals (remember in the old days you could only obtain them via Class.forName()). I have wondered the same thing, though I also appreciate that the devil is in the details. For example, David's question could be addressed by adding Method and Field literals: public class Foo { public static void meth(int x) { ... } } Method m = Foo::meth.method; m.invoke(123); Of course, this is still imprecise because Method is not generic and so this would fail if there were overloads, among other problems. So I think the intuition is valid. Whether a practical solution exists is another question. -Archie On Mon, Dec 1, 2025 at 10:46?AM Chen Liang wrote: > Hi David, before we investigate solutions, what is the problem you want to > resolve? > If you want to obtain a Method on a record class, you can do > recordClass.getMethod("variable") to find that accessor method. > Note that the Java Virtual Machine does not validate a record, that it may > recognize a class with the record attribute but has no corresponding > accessor methods or canonical constructor as a record class. > -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Mon Dec 1 18:10:10 2025 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Mon, 1 Dec 2025 19:10:10 +0100 Subject: Feature Request -- Enumerate the RecordComponents of a record In-Reply-To: References: Message-ID: Hi, My concern with the actual proposal (not with the problem it is trying to solve) is that it doesn't generalize well. That is, imagine if function types get added to the language with support to query the parameters, etc. of the underlying function. Then immediately this change becomes a useless (in fact, harmful) noise. So, let me give a counter proposal (even though I'm quite certain it will not be added anytime soon given all the important stuff in the queue): Allow the following construct: ``` RecordComponentAccessor accessor = MyRecord::myComponent; ``` where the `RecordComponentAccessor` could look like it (of course, doesn't have to be an interface): ``` interface RecordComponentAccessor { Type recordType(); Class recordRawType(); String componentName(); Type componentType(); Class componentRawType(); MethodHandle getter(); default V getValue(R record) { // TODO: Deal with `throws Throwable` return getter().invoke(record); } } ``` The benefit of this is that, if function types ever become a thing, then of course this can be retrofitted to implement (or extend) the function type `(R) -> V` (or whatever notation it would have), which would likely have more generic methods like `List> parameterRawTypes()`, etc. In my opinion, this is not harder to implement than the original proposal, and more future proof. And if it is only implemented for records, then no massive changes to the JVM needed (unlike with the introduction of a full blown function type). Attila David Alayachew ezt ?rta (id?pont: 2025. nov. 30., V, 22:51): > Hello @amber-dev , > > (this is a follow-up from the previous thread -- > https://mail.openjdk.org/pipermail/amber-dev/2025-November/009472.html) > > Since records are *transparent* carriers of data, then that means that > all of the record components are known at compile time and visible to all > consumers who can reach the record itself. > > Right now, the only way to reach these record components is by drilling > down via j.l.Class ---> j.l.r.RecordComponent. And even then, you are > forced into doing String comparison against what you *expect* the record > component to be named. That means that you will get a runtime error, not a > compile time error, if a record component changes names. > > I propose that we enumerate the Record Components of a record. > > My naive way of accomplishing this would be to literally provide each > record with its own inner enum, each value corresponding to the respective > record component on a record. > > Consider the following record. > > record User(String firstName, String lastName, int age) {} > > I ask that record get access to its own inner enum (let's call it VALUES), > that can be referenced. Like this. > > record User(String firstName, String lastName, int age) > { > enum Values { > firstName, > lastName, > age, > ; > public final java.lang.reflect.RecordComponent recordComponent = > Arrays > .stream(User.class.getRecordComponents()) > .filter(rc -> rc.getName().equals(this.name())) > .findAny() > .orElseThrow() > ; > } > } > > This is better than the current situation for all sorts of reasons. > > 1. Currently, if I want a RecordComponent, I must make a > String-comparison. I lose compile time safety of checking if my hard-coded > string no longer matches because someone changed the record. > 2. I now get exhaustiveness checking, which really should have been > there from the beginning. > > And of course, I am not tied to the idea of using an enum or an inner > class. My real goal is to be able to enumerate over the components of a > record. Not even necessarily over the j.l.r.RecordComponents of a record. > Whatever form that takes is fine with me. > > But not being able to enumerate over a records components (obviously, in a > type-safe, non-*stringly*-typed way) is making records less powerful for > seemingly no reason. The contract and spec enables it, it's just not being > utilized. > > Thank you for your time and consideration. > David Alayachew > -------------- next part -------------- An HTML attachment was scrubbed... URL: From brian.goetz at oracle.com Mon Dec 1 18:37:56 2025 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 1 Dec 2025 18:37:56 +0000 Subject: Reflection on records In-Reply-To: References: Message-ID: Let me replay some analysis that was done on this topic in the past. There are two separate things going on here: - Can we use symbolic constants instead of string constants to do reflective lookups - Can we have more kinds of symbolic reflective constants (field literals, method literals, record component literals, method handle literals, etc.) The first one is more shallow; David is exploiting a special case of records where (a) there is already a method reference form for accessors, which could potentially be bootstrapped into service here and (b) very luckily, the shape of all such accessor method references vary only parametrically in shape. So one could write a method: interface AccessorShape { U access(T t); } RecordComponent component(Accessor c) { ? } Which would allow us to say RecordComponent rc = component(Foo::component) Where the parameter is a method reference but from which we can strip-mine the name and type and turn it into a component lookup. That?s a clever trick, and there is no intrinsic reason why we couldn?t do this, but it doesn?t scale beyond this particular case. Field and method lookups would still use strings and could fail at runtime for all the reasons. The second is one that was explored fairly deeply during Lambda. Reflective literals for jlr.{Field,Method,Constructor} are an obvious move, and have a few challenges, some of which are workable, but the biggest of which is: overloading. With method reference targeted at functional interfaces, the functional interface provides a signature shape with which we can do overload selection. But if we had an overloaded method m, then Method m = Foo::m We have no information with which to select the proper m. (Please, don?t take this as an invitation for a syntax discussion; they?ve all been explored, and besides, there are deeper problems here than syntax.) On Dec 1, 2025, at 12:13 PM, Archie Cobbs > wrote: I'm guessing what underlies David's question is this thought: Why can't the compiler provide more compile-time checking of Field and Method reflection objects? After all, it does this for Class objects by providing us with Class literals (remember in the old days you could only obtain them via Class.forName()). I have wondered the same thing, though I also appreciate that the devil is in the details. For example, David's question could be addressed by adding Method and Field literals: public class Foo { public static void meth(int x) { ... } } Method m = Foo::meth.method; m.invoke(123); Of course, this is still imprecise because Method is not generic and so this would fail if there were overloads, among other problems. So I think the intuition is valid. Whether a practical solution exists is another question. -Archie On Mon, Dec 1, 2025 at 10:46?AM Chen Liang > wrote: Hi David, before we investigate solutions, what is the problem you want to resolve? If you want to obtain a Method on a record class, you can do recordClass.getMethod("variable") to find that accessor method. Note that the Java Virtual Machine does not validate a record, that it may recognize a class with the record attribute but has no corresponding accessor methods or canonical constructor as a record class. -- Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From artyomcool2 at gmail.com Mon Dec 1 18:59:02 2025 From: artyomcool2 at gmail.com (Artyom Drozdov) Date: Mon, 1 Dec 2025 19:59:02 +0100 Subject: Reflection on records In-Reply-To: References: Message-ID: Symbolic reflective constants is a something I was always dreamed about. I'm currently using old-but-gold CLDC HI for my pet project and I'm really struggling without class literals (so I'll probably add them). The same way I feel about fields/methods references. I suppose it was discussed before, but it would be great to know, why can't we ask user for the full signature? Like: Method m = Foo::method[String, int]; Or even (argument names here is an optional hint for the reader and IDE/linters): Method m = Foo::method[String name, int size]; Of course, the syntax is not something that really worth to discuss here. Probably, we might want to specify the return type as well, but, again, that's about syntax. ??, 1 ???. 2025??., 19:38 Brian Goetz : > Let me replay some analysis that was done on this topic in the past. > > There are two separate things going on here: > > - Can we use symbolic constants instead of string constants to do > reflective lookups > - Can we have more kinds of symbolic reflective constants (field > literals, method literals, record component literals, method handle > literals, etc.) > > The first one is more shallow; David is exploiting a special case of > records where (a) there is already a method reference form for accessors, > which could potentially be bootstrapped into service here and (b) very > luckily, the shape of all such accessor method references vary only > parametrically in shape. So one could write a method: > > interface AccessorShape { > U access(T t); > } > > RecordComponent component(Accessor c) { ? } > > Which would allow us to say > > RecordComponent rc = component(Foo::component) > > Where the parameter is a method reference but from which we can strip-mine > the name and type and turn it into a component lookup. That?s a clever > trick, and there is no intrinsic reason why we couldn?t do this, but it > doesn?t scale beyond this particular case. Field and method lookups would > still use strings and could fail at runtime for all the reasons. > > The second is one that was explored fairly deeply during Lambda. > Reflective literals for jlr.{Field,Method,Constructor} are an obvious move, > and have a few challenges, some of which are workable, but the biggest of > which is: overloading. With method reference targeted at functional > interfaces, the functional interface provides a signature shape with which > we can do overload selection. But if we had an overloaded method m, then > > Method m = Foo::m > > We have no information with which to select the proper m. (Please, don?t > take this as an invitation for a syntax discussion; they?ve all been > explored, and besides, there are deeper problems here than syntax.) > > > On Dec 1, 2025, at 12:13 PM, Archie Cobbs wrote: > > I'm guessing what underlies David's question is this thought: Why can't > the compiler provide more compile-time checking of Field and > Method reflection objects? > > After all, it does this for Class objects by providing us with Class > literals (remember in the old days you could only obtain them via > Class.forName()). > > I have wondered the same thing, though I also appreciate that the devil is > in the details. > > For example, David's question could be addressed by adding Method and > Field literals: > > public class Foo { > public static void meth(int x) { ... } > } > > Method m = Foo::meth.method; > m.invoke(123); > > Of course, this is still imprecise because Method is not generic and so > this would fail if there were overloads, among other problems. > > So I think the intuition is valid. Whether a practical solution exists is > another question. > > -Archie > > On Mon, Dec 1, 2025 at 10:46?AM Chen Liang > wrote: > >> Hi David, before we investigate solutions, what is the problem you want >> to resolve? >> If you want to obtain a Method on a record class, you can do >> recordClass.getMethod("variable") to find that accessor method. >> Note that the Java Virtual Machine does not validate a record, that it >> may recognize a class with the record attribute but has no corresponding >> accessor methods or canonical constructor as a record class. >> > -- > Archie L. Cobbs > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From forax at univ-mlv.fr Mon Dec 1 19:00:31 2025 From: forax at univ-mlv.fr (Remi Forax) Date: Mon, 1 Dec 2025 20:00:31 +0100 (CET) Subject: Reflection on records In-Reply-To: References: Message-ID: <50578952.52395295.1764615631422.JavaMail.zimbra@univ-eiffel.fr> > From: "Brian Goetz" > To: "Archie Cobbs" > Cc: "Chen Liang" , "David Alayachew" > , "amber-dev" > Sent: Monday, December 1, 2025 7:37:56 PM > Subject: Re: Reflection on records > Let me replay some analysis that was done on this topic in the past. > There are two separate things going on here: > - Can we use symbolic constants instead of string constants to do reflective > lookups > - Can we have more kinds of symbolic reflective constants (field literals, > method literals, record component literals, method handle literals, etc.) > The first one is more shallow; David is exploiting a special case of records > where (a) there is already a method reference form for accessors, which could > potentially be bootstrapped into service here and (b) very luckily, the shape > of all such accessor method references vary only parametrically in shape. So > one could write a method: > interface AccessorShape { > U access(T t); > } > RecordComponent component(Accessor c) { ? } > Which would allow us to say > RecordComponent rc = component(Foo::component) > Where the parameter is a method reference but from which we can strip-mine the > name and type and turn it into a component lookup. That?s a clever trick, and > there is no intrinsic reason why we couldn?t do this, but it doesn?t scale > beyond this particular case. Field and method lookups would still use strings > and could fail at runtime for all the reasons. > The second is one that was explored fairly deeply during Lambda. Reflective > literals for jlr.{Field,Method,Constructor} are an obvious move, and have a few > challenges, some of which are workable, but the biggest of which is: > overloading. With method reference targeted at functional interfaces, the > functional interface provides a signature shape with which we can do overload > selection. But if we had an overloaded method m, then > Method m = Foo::m > We have no information with which to select the proper m. (Please, don?t take > this as an invitation for a syntax discussion; they?ve all been explored, and > besides, there are deeper problems here than syntax.) yes, the current status quo is that IDEs (at least IntelliJ) are able to typecheck the creation of Method, Field, MethodHandle, VarHandle, etc so it's not in the language but it's good enough. We still have to make all those pesky exceptions unchecked but that's another story ... R?mi >> On Dec 1, 2025, at 12:13 PM, Archie Cobbs < [ mailto:archie.cobbs at gmail.com | >> archie.cobbs at gmail.com ] > wrote: >> I'm guessing what underlies David's question is this thought: Why can't the >> compiler provide more compile-time checking of Field and Method reflection >> objects? >> After all, it does this for Class objects by providing us with Class literals >> (remember in the old days you could only obtain them via Class.forName() ). >> I have wondered the same thing, though I also appreciate that the devil is in >> the details. >> For example, David's question could be addressed by adding Method and Field >> literals: >> public class Foo { >> public static void meth(int x) { ... } >> } >> Method m = Foo::meth.method; >> m.invoke(123); >> Of course, this is still imprecise because Method is not generic and so this >> would fail if there were overloads, among other problems. >> So I think the intuition is valid. Whether a practical solution exists is >> another question. >> -Archie >> On Mon, Dec 1, 2025 at 10:46 AM Chen Liang < [ mailto:chen.l.liang at oracle.com | >> chen.l.liang at oracle.com ] > wrote: >>> Hi David, before we investigate solutions, what is the problem you want to >>> resolve? >>> If you want to obtain a Method on a record class, you can do >>> recordClass.getMethod("variable") to find that accessor method. >>> Note that the Java Virtual Machine does not validate a record, that it may >>> recognize a class with the record attribute but has no corresponding accessor >>> methods or canonical constructor as a record class. >> -- >> Archie L. Cobbs -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Mon Dec 1 19:17:54 2025 From: amaembo at gmail.com (Tagir Valeev) Date: Mon, 1 Dec 2025 20:17:54 +0100 Subject: Reflection on records In-Reply-To: <50578952.52395295.1764615631422.JavaMail.zimbra@univ-eiffel.fr> References: <50578952.52395295.1764615631422.JavaMail.zimbra@univ-eiffel.fr> Message-ID: Hello! On Mon, Dec 1, 2025, 20:01 Remi Forax wrote: > the current status quo is that IDEs (at least IntelliJ) are able to > typecheck the creation of Method, Field, MethodHandle, VarHandle, etc so > it's not in the language but it's good enough. > Yes, thank you, Remi! David said that he wants to avoid stringly-typed code, but what is string is solely a question of a tool you use to interpret your program. For an old-school text editor, the whole Java program is simply a string. For example, a typo in a called method name will be unnoticed by such a tool in the same way, as a typo inside a string literal. The Java compiler understands proper Java identifiers but doesn't understand Java strings. A good IDE goes further: it treats certain strings as full-fledged references. When you type MyRecord.class.getField("myComponent"), you get code completion, inspection highlighting of incorrect reference (distinct from compilation error and suppressable, of course), find usage, navigation to the declaration (you can jump from "myComponent" string to an actual component and back). You can even initiate a 'Rename' refactoring inside the string, and the component will be renamed! From the tool point of view, these strings are first-class citizens. So it's not always necessary to change your code to avoid being stringly-typed. Instead, use proper tools or convince your tool vendor to add first-class reflective references support. With best regards, Tagir Valeev -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Mon Dec 1 19:21:23 2025 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Mon, 1 Dec 2025 20:21:23 +0100 Subject: Reflection on records In-Reply-To: References: Message-ID: One thing I can think of is that generics makes life a hell here, and can spawn a lot of reasonable questions with non-obvious answers (especially if you still believe in reified generics in the future, and try to prepare for it). So, if you have ``` interface Foo { String hello(T a); } ``` Then if you write (in your notation): `Foo::hello[T]`, where T is a type argument, then you have to return `Object.class` as the parameter type. However, if you have `Foo::hello[String]`, then one could reasonably expect it to return `String.class` as the parameter type, but doing so would also be strange, because the actual type (queried via reflection) would be `Object.class`. Not to mention that `hello` can be overloaded with a concrete type, but I guess that is a theoretical problem even in today's Java. Attila Artyom Drozdov ezt ?rta (id?pont: 2025. dec. 1., H, 19:59): > Symbolic reflective constants is a something I was always dreamed about. > I'm currently using old-but-gold CLDC HI for my pet project and I'm really > struggling without class literals (so I'll probably add them). > The same way I feel about fields/methods references. I suppose it was > discussed before, but it would be great to know, why can't we ask user for > the full signature? Like: > > Method m = Foo::method[String, int]; > > Or even (argument names here is an optional hint for the reader and > IDE/linters): > > Method m = Foo::method[String name, int size]; > > Of course, the syntax is not something that really worth to discuss here. > Probably, we might want to specify the return type as well, but, again, > that's about syntax. > >> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From artyomcool2 at gmail.com Mon Dec 1 19:35:05 2025 From: artyomcool2 at gmail.com (Artyom Drozdov) Date: Mon, 1 Dec 2025 20:35:05 +0100 Subject: Reflection on records In-Reply-To: References: Message-ID: Yes, it sounds like an issue without good solution. For me sounds ok for your example having just raw types: Method hello = Foo::hello[Object]; But I understand it could be non-intuitive for most users. Another approach would be to allow your notation: Method hello = Foo::hello[T]; But disallow specifying types like: Method helloString = Foo::hello[String]; ??, 1 ???. 2025??., 20:21 Attila Kelemen : > One thing I can think of is that generics makes life a hell here, and can > spawn a lot of reasonable questions with non-obvious answers (especially if > you still believe in reified generics in the future, and try to prepare for > it). > > So, if you have > > ``` > interface Foo { String hello(T a); } > ``` > > Then if you write (in your notation): `Foo::hello[T]`, where T is a > type argument, then you have to return `Object.class` as the parameter type. > > However, if you have `Foo::hello[String]`, then one could > reasonably expect it to return `String.class` as the parameter type, but > doing so would also be strange, because the actual type (queried via > reflection) would be `Object.class`. > > Not to mention that `hello` can be overloaded with a concrete type, but I > guess that is a theoretical problem even in today's Java. > > Attila > > > Artyom Drozdov ezt ?rta (id?pont: 2025. dec. 1., > H, 19:59): > >> Symbolic reflective constants is a something I was always dreamed about. >> I'm currently using old-but-gold CLDC HI for my pet project and I'm >> really struggling without class literals (so I'll probably add them). >> The same way I feel about fields/methods references. I suppose it was >> discussed before, but it would be great to know, why can't we ask user for >> the full signature? Like: >> >> Method m = Foo::method[String, int]; >> >> Or even (argument names here is an optional hint for the reader and >> IDE/linters): >> >> Method m = Foo::method[String name, int size]; >> >> Of course, the syntax is not something that really worth to discuss here. >> Probably, we might want to specify the return type as well, but, again, >> that's about syntax. >> >>> >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From attila.kelemen85 at gmail.com Mon Dec 1 19:52:48 2025 From: attila.kelemen85 at gmail.com (Attila Kelemen) Date: Mon, 1 Dec 2025 20:52:48 +0100 Subject: Reflection on records In-Reply-To: References: Message-ID: Also, by the way, these method literals have limited usefulness, if you just declare them `Method` or `MethodHandle`, because sure the compiler can check the method name, but it won't be able to check for the number of parameters and their type, and so sure, a little better, but not that much. If method literals (etc) would be a thing, then I would expect arbitrary function types with them as well (which could have a common base of course). Like `function`, and in this case, accepting the raw type becomes a lot more problematic, because returning `Object.class` when a declaration clearly states `String` is a bit troubling (for me it is for sure). Artyom Drozdov ezt ?rta (id?pont: 2025. dec. 1., H, 20:35): > Yes, it sounds like an issue without good solution. > For me sounds ok for your example having just raw types: > > Method hello = Foo::hello[Object]; > > But I understand it could be non-intuitive for most users. Another > approach would be to allow your notation: > > Method hello = Foo::hello[T]; > > But disallow specifying types like: > > Method helloString = Foo::hello[String]; > > > ??, 1 ???. 2025??., 20:21 Attila Kelemen : > >> One thing I can think of is that generics makes life a hell here, and can >> spawn a lot of reasonable questions with non-obvious answers (especially if >> you still believe in reified generics in the future, and try to prepare for >> it). >> >> So, if you have >> >> ``` >> interface Foo { String hello(T a); } >> ``` >> >> Then if you write (in your notation): `Foo::hello[T]`, where T is a >> type argument, then you have to return `Object.class` as the parameter type. >> >> However, if you have `Foo::hello[String]`, then one could >> reasonably expect it to return `String.class` as the parameter type, but >> doing so would also be strange, because the actual type (queried via >> reflection) would be `Object.class`. >> >> Not to mention that `hello` can be overloaded with a concrete type, but I >> guess that is a theoretical problem even in today's Java. >> >> Attila >> >> >> Artyom Drozdov ezt ?rta (id?pont: 2025. dec. 1., >> H, 19:59): >> >>> Symbolic reflective constants is a something I was always dreamed about. >>> I'm currently using old-but-gold CLDC HI for my pet project and I'm >>> really struggling without class literals (so I'll probably add them). >>> The same way I feel about fields/methods references. I suppose it was >>> discussed before, but it would be great to know, why can't we ask user for >>> the full signature? Like: >>> >>> Method m = Foo::method[String, int]; >>> >>> Or even (argument names here is an optional hint for the reader and >>> IDE/linters): >>> >>> Method m = Foo::method[String name, int size]; >>> >>> Of course, the syntax is not something that really worth to discuss >>> here. Probably, we might want to specify the return type as well, but, >>> again, that's about syntax. >>> >>>> >>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Dec 1 20:09:06 2025 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 1 Dec 2025 15:09:06 -0500 Subject: Reflection on records In-Reply-To: References: Message-ID: Oh wow, thank you all for the responses! @Chen Liang - Thanks for asking this. I am asking an X-Y problem. Let me quickly summarize both the X and Y. For the X, I want a j.l.r.RecordComponent, but I don't want to ask my clients to pass around String literals or do String comparison that could get out-of-sync on later compilations. Method references looked like the way to get there. For the Y, what I really want is some way to reference a records components, and be informed at compile time when my use of them is out-of-date/sync. @Ethan McCue - ty vm for the link, I remember reading that, but forgot it while typing this one. Nice complement to this thread. @Archie Cobbs and @Brian Goetz - Thanks for highlighting the gist of my question. I do see the pain point of method overloads, so I understand if that is the nail in the coffin here for my idea. @Remi Forax and @Tagir Valeev - Thanks for the IDE perspective. But I'm curious -- if I update my record by changing one of its components type or name, would all of those string references to that component become red squiggles? And what about if there is a layer of indirection, like passing the string through a helper method? Or multiple layers of methods? Thanks for the responses everyone! On Mon, Dec 1, 2025, 2:53?PM Attila Kelemen wrote: > Also, by the way, these method literals have limited usefulness, if you > just declare them `Method` or `MethodHandle`, because sure the compiler can > check the method name, but it won't be able to check for the number of > parameters and their type, and so sure, a little better, but not that much. > If method literals (etc) would be a thing, then I would expect arbitrary > function types with them as well (which could have a common base of > course). Like `function`, and in this case, accepting > the raw type becomes a lot more problematic, because returning > `Object.class` when a declaration clearly states `String` is a bit > troubling (for me it is for sure). > > Artyom Drozdov ezt ?rta (id?pont: 2025. dec. 1., > H, 20:35): > >> Yes, it sounds like an issue without good solution. >> For me sounds ok for your example having just raw types: >> >> Method hello = Foo::hello[Object]; >> >> But I understand it could be non-intuitive for most users. Another >> approach would be to allow your notation: >> >> Method hello = Foo::hello[T]; >> >> But disallow specifying types like: >> >> Method helloString = Foo::hello[String]; >> >> >> ??, 1 ???. 2025??., 20:21 Attila Kelemen : >> >>> One thing I can think of is that generics makes life a hell here, and >>> can spawn a lot of reasonable questions with non-obvious answers >>> (especially if you still believe in reified generics in the future, and try >>> to prepare for it). >>> >>> So, if you have >>> >>> ``` >>> interface Foo { String hello(T a); } >>> ``` >>> >>> Then if you write (in your notation): `Foo::hello[T]`, where T is a >>> type argument, then you have to return `Object.class` as the parameter type. >>> >>> However, if you have `Foo::hello[String]`, then one could >>> reasonably expect it to return `String.class` as the parameter type, but >>> doing so would also be strange, because the actual type (queried via >>> reflection) would be `Object.class`. >>> >>> Not to mention that `hello` can be overloaded with a concrete type, but >>> I guess that is a theoretical problem even in today's Java. >>> >>> Attila >>> >>> >>> Artyom Drozdov ezt ?rta (id?pont: 2025. dec. >>> 1., H, 19:59): >>> >>>> Symbolic reflective constants is a something I was always dreamed about. >>>> I'm currently using old-but-gold CLDC HI for my pet project and I'm >>>> really struggling without class literals (so I'll probably add them). >>>> The same way I feel about fields/methods references. I suppose it was >>>> discussed before, but it would be great to know, why can't we ask user for >>>> the full signature? Like: >>>> >>>> Method m = Foo::method[String, int]; >>>> >>>> Or even (argument names here is an optional hint for the reader and >>>> IDE/linters): >>>> >>>> Method m = Foo::method[String name, int size]; >>>> >>>> Of course, the syntax is not something that really worth to discuss >>>> here. Probably, we might want to specify the return type as well, but, >>>> again, that's about syntax. >>>> >>>>> >>>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Mon Dec 1 20:15:54 2025 From: davidalayachew at gmail.com (David Alayachew) Date: Mon, 1 Dec 2025 15:15:54 -0500 Subject: Feature Request -- Enumerate the RecordComponents of a record In-Reply-To: References: Message-ID: Thanks for the response @Attila Kelemen . Yes, I am definitely not married to the concept of using an enum or an inner class. There are clear flaws with it, and I only use it to explain what I really want. Really, all I want is to be able to enumerate the components of a record and also get exhaustiveness checking. As for your implementation suggestion, no real comments. As long as it works, I am fine with any implementation. On Mon, Dec 1, 2025, 1:10?PM Attila Kelemen wrote: > Hi, > > My concern with the actual proposal (not with the problem it is trying to > solve) is that it doesn't generalize well. That is, imagine if function > types get added to the language with support to query the parameters, etc. > of the underlying function. Then immediately this change becomes a useless > (in fact, harmful) noise. > > So, let me give a counter proposal (even though I'm quite certain it will > not be added anytime soon given all the important stuff in the queue): > > Allow the following construct: > > ``` > RecordComponentAccessor accessor = MyRecord::myComponent; > ``` > > where the `RecordComponentAccessor` could look like it (of course, doesn't > have to be an interface): > > ``` > interface RecordComponentAccessor { > Type recordType(); > Class recordRawType(); > > String componentName(); > > Type componentType(); > Class componentRawType(); > > MethodHandle getter(); > > default V getValue(R record) { > // TODO: Deal with `throws Throwable` > return getter().invoke(record); > } > } > ``` > > The benefit of this is that, if function types ever become a thing, then > of course this can be retrofitted to implement (or extend) the function > type `(R) -> V` (or whatever notation it would have), which would likely > have more generic methods like `List> parameterRawTypes()`, etc. > > In my opinion, this is not harder to implement than the original proposal, > and more future proof. And if it is only implemented for records, then no > massive changes to the JVM needed (unlike with the introduction of a full > blown function type). > > Attila > > David Alayachew ezt ?rta (id?pont: 2025. nov. > 30., V, 22:51): > >> Hello @amber-dev , >> >> (this is a follow-up from the previous thread -- >> https://mail.openjdk.org/pipermail/amber-dev/2025-November/009472.html) >> >> Since records are *transparent* carriers of data, then that means that >> all of the record components are known at compile time and visible to all >> consumers who can reach the record itself. >> >> Right now, the only way to reach these record components is by drilling >> down via j.l.Class ---> j.l.r.RecordComponent. And even then, you are >> forced into doing String comparison against what you *expect* the record >> component to be named. That means that you will get a runtime error, not a >> compile time error, if a record component changes names. >> >> I propose that we enumerate the Record Components of a record. >> >> My naive way of accomplishing this would be to literally provide each >> record with its own inner enum, each value corresponding to the respective >> record component on a record. >> >> Consider the following record. >> >> record User(String firstName, String lastName, int age) {} >> >> I ask that record get access to its own inner enum (let's call it >> VALUES), that can be referenced. Like this. >> >> record User(String firstName, String lastName, int age) >> { >> enum Values { >> firstName, >> lastName, >> age, >> ; >> public final java.lang.reflect.RecordComponent recordComponent = >> Arrays >> .stream(User.class.getRecordComponents()) >> .filter(rc -> rc.getName().equals(this.name())) >> .findAny() >> .orElseThrow() >> ; >> } >> } >> >> This is better than the current situation for all sorts of reasons. >> >> 1. Currently, if I want a RecordComponent, I must make a >> String-comparison. I lose compile time safety of checking if my hard-coded >> string no longer matches because someone changed the record. >> 2. I now get exhaustiveness checking, which really should have been >> there from the beginning. >> >> And of course, I am not tied to the idea of using an enum or an inner >> class. My real goal is to be able to enumerate over the components of a >> record. Not even necessarily over the j.l.r.RecordComponents of a record. >> Whatever form that takes is fine with me. >> >> But not being able to enumerate over a records components (obviously, in >> a type-safe, non-*stringly*-typed way) is making records less powerful >> for seemingly no reason. The contract and spec enables it, it's just not >> being utilized. >> >> Thank you for your time and consideration. >> David Alayachew >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From artyomcool2 at gmail.com Mon Dec 1 20:20:18 2025 From: artyomcool2 at gmail.com (Artyom Drozdov) Date: Mon, 1 Dec 2025 21:20:18 +0100 Subject: Reflection on records In-Reply-To: References: Message-ID: If you have a way to declare the complete function type, you don't have the overload and generic issues anymore, like it is already done for lamdas. Maybe you are right and the first (and more important) thing would be type safe MethodHandles. Or maybe we could utilize something similar with the approach you suggested before. What if we introduce Serializable-like interface, that would allow us introspection of instance of FunctionalInterface? Runnable hello = (Runnable & Introspectable)Foo::hello; This allows us to deduct the signature (as we already do) and have extra runtime information via Introspectable interface, that could contain reflective information, like method name, argument types, etc. ??, 1 ???. 2025??., 20:53 Attila Kelemen : > Also, by the way, these method literals have limited usefulness, if you > just declare them `Method` or `MethodHandle`, because sure the compiler can > check the method name, but it won't be able to check for the number of > parameters and their type, and so sure, a little better, but not that much. > If method literals (etc) would be a thing, then I would expect arbitrary > function types with them as well (which could have a common base of > course). Like `function`, and in this case, accepting > the raw type becomes a lot more problematic, because returning > `Object.class` when a declaration clearly states `String` is a bit > troubling (for me it is for sure). > > Artyom Drozdov ezt ?rta (id?pont: 2025. dec. 1., > H, 20:35): > >> Yes, it sounds like an issue without good solution. >> For me sounds ok for your example having just raw types: >> >> Method hello = Foo::hello[Object]; >> >> But I understand it could be non-intuitive for most users. Another >> approach would be to allow your notation: >> >> Method hello = Foo::hello[T]; >> >> But disallow specifying types like: >> >> Method helloString = Foo::hello[String]; >> >> >> ??, 1 ???. 2025??., 20:21 Attila Kelemen : >> >>> One thing I can think of is that generics makes life a hell here, and >>> can spawn a lot of reasonable questions with non-obvious answers >>> (especially if you still believe in reified generics in the future, and try >>> to prepare for it). >>> >>> So, if you have >>> >>> ``` >>> interface Foo { String hello(T a); } >>> ``` >>> >>> Then if you write (in your notation): `Foo::hello[T]`, where T is a >>> type argument, then you have to return `Object.class` as the parameter type. >>> >>> However, if you have `Foo::hello[String]`, then one could >>> reasonably expect it to return `String.class` as the parameter type, but >>> doing so would also be strange, because the actual type (queried via >>> reflection) would be `Object.class`. >>> >>> Not to mention that `hello` can be overloaded with a concrete type, but >>> I guess that is a theoretical problem even in today's Java. >>> >>> Attila >>> >>> >>> Artyom Drozdov ezt ?rta (id?pont: 2025. dec. >>> 1., H, 19:59): >>> >>>> Symbolic reflective constants is a something I was always dreamed about. >>>> I'm currently using old-but-gold CLDC HI for my pet project and I'm >>>> really struggling without class literals (so I'll probably add them). >>>> The same way I feel about fields/methods references. I suppose it was >>>> discussed before, but it would be great to know, why can't we ask user for >>>> the full signature? Like: >>>> >>>> Method m = Foo::method[String, int]; >>>> >>>> Or even (argument names here is an optional hint for the reader and >>>> IDE/linters): >>>> >>>> Method m = Foo::method[String name, int size]; >>>> >>>> Of course, the syntax is not something that really worth to discuss >>>> here. Probably, we might want to specify the return type as well, but, >>>> again, that's about syntax. >>>> >>>>> >>>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From amaembo at gmail.com Tue Dec 2 09:48:12 2025 From: amaembo at gmail.com (Tagir Valeev) Date: Tue, 2 Dec 2025 10:48:12 +0100 Subject: Reflection on records In-Reply-To: References: Message-ID: Ok, I've managed to implement a working Babylon-based solution. Here's the library code: package org.example; import jdk.incubator.code.Quoted; import jdk.incubator.code.Reflect; import jdk.incubator.code.dialect.core.CoreOp; import jdk.incubator.code.dialect.java.JavaOp; import jdk.incubator.code.dialect.java.MethodRef; import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; import java.lang.reflect.RecordComponent; import java.util.Arrays; public final class ReflectUtil { @Reflect @FunctionalInterface public interface RecordAccessor { /** * @param r record type * @return result of the record component * @param an unused generic parameter to avoid accidental use with lambda * (lambdas cannot bind to generic SAM). * Only method references are supported. */ Object get(T r); } /** * @param accessor a method reference bound to a record accessor * @return record component that corresponds to a given accessor * @param record type * @throws IllegalArgumentException if the supplied argument is not a record accessor method reference * @throws RuntimeException if the record accessor cannot be resolved (e.g., due to insufficient access rights) */ public static RecordComponent component(RecordAccessor accessor) { Quoted quoted = CoreOp.QuotedOp.ofQuotable(accessor) .orElseThrow(() -> new IllegalArgumentException("Accessor must be quotable")); if (!(quoted.op() instanceof JavaOp.LambdaOp lambda)) { throw new IllegalArgumentException("Expected method reference"); } JavaOp.InvokeOp invokeOp = lambda.methodReference() .orElseThrow(() -> new IllegalArgumentException("Expected method reference")); if (invokeOp.invokeKind() != JavaOp.InvokeOp.InvokeKind.INSTANCE) { throw new IllegalArgumentException("Method reference bound to record accessor is expected"); } MethodRef descriptor = invokeOp.invokeDescriptor(); Method method; try { method = descriptor.resolveToMethod(MethodHandles.privateLookupIn(accessor.getClass(), MethodHandles.lookup())); } catch (ReflectiveOperationException e) { throw new RuntimeException("Cannot resolve descriptor "+descriptor, e); } Class recordClass = method.getDeclaringClass(); if (!recordClass.isRecord()) { throw new IllegalArgumentException("Not a record accessor method"); } return Arrays.stream(recordClass.getRecordComponents()).filter(rc -> rc.getAccessor().equals(method)) .findFirst() .orElseThrow(() -> new IllegalArgumentException("Not a record accessor: "+method)); } } It could be used like this: package org.example; public class Main { private record MyRecord(int comp, double comp2) {} static void main() { IO.println(ReflectUtil.component(MyRecord::comp)); IO.println(ReflectUtil.component(MyRecord::comp2)); } } You have to open your module to code-reflection though: module reflectRecords { requires jdk.incubator.code; opens org.example to jdk.incubator.code; } With best regards, Tagir Valeev On Mon, Dec 1, 2025 at 3:09?PM Tagir Valeev wrote: > I don't think there are any published JEPs. Here's some information about > the project: > https://openjdk.org/projects/babylon/ > > On Mon, Dec 1, 2025, 15:08 David Alayachew > wrote: > >> Understood. Thanks for clarifying. For now, I'll hold off on that, as I >> don't know how stable that is, and I don't really want to.build this new >> library off of it. Maybe when the respective JEP goes into preview. Could >> you link me to that JEP, or is there not one yet? >> >> On Mon, Dec 1, 2025, 9:01?AM Tagir Valeev wrote: >> >>> Yes, you have to build it from the Babylon repo. >>> >>> On Mon, Dec 1, 2025, 14:35 David Alayachew >>> wrote: >>> >>>> Thanks @Tagir Valeev . >>>> >>>> I am still not great at the stuff Babylon is doing, so I can only >>>> roughly follow along. Regardless, it sounds like this is depending on >>>> Babylon features which have not yet landed in the mainline jdk, yes? >>>> >>>> >>>> On Mon, Dec 1, 2025, 5:22?AM Tagir Valeev wrote: >>>> >>>>> I think this will surely be possible for RecordComponents with code >>>>> reflection (project Babylon), using the syntax you propose. >>>>> >>>>> You can create a quotable function like this: >>>>> >>>>> import jdk.incubator.code.*; >>>>> >>>>> @Reflect >>>>> @FunctionalInterface >>>>> interface Accessor { >>>>> Object get(Record r); >>>>> } >>>>> >>>>> Then create something like (just a sketch, sorry, I have no time now >>>>> to provide a full-fledged sample): >>>>> >>>>> static RecordComponent foo(Accessor acc) { >>>>> Quoted q = Op.ofQuotable(acc).orElseThrow(); >>>>> // extract a method handle from q using Babylon API >>>>> // unreflect it and find the corresponding RecordComponent >>>>> } >>>>> >>>>> With best regards, >>>>> Tagir Valeev >>>>> >>>>> On Sat, Nov 29, 2025 at 8:45?PM David Alayachew < >>>>> davidalayachew at gmail.com> wrote: >>>>> >>>>>> Hello @amber-dev , >>>>>> >>>>>> I asked this on core-libs-dev already, but I figured I should ask >>>>>> here too. >>>>>> >>>>>> Let's say I have some record User(String firstName, String lastName) >>>>>> {}. >>>>>> >>>>>> Is there any possible way for me to do the following? >>>>>> >>>>>> java.lang.reflect.RecordComponent firstName = foo(User::firstName); >>>>>> >>>>>> I'll even accept this. >>>>>> >>>>>> java.lang.reflect.Method lastName = foo(User::lastName); >>>>>> >>>>>> Thank you for your time and consideration. >>>>>> David Alayachew >>>>>> >>>>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.1993grajales at gmail.com Fri Dec 5 04:38:14 2025 From: david.1993grajales at gmail.com (david Grajales) Date: Thu, 4 Dec 2025 23:38:14 -0500 Subject: Feedback about LazyConstants API (JEP526) Message-ID: Dear Java Dev Team, I am writing to provide feedback and two specific observations regarding the LazyConstant API, which is currently a preview feature in OpenJDK 26. I appreciate the API's direction and I think it's a good improvement compared to its first iteration; however, I see potential for improved expressiveness, particularly in conditional scenarios. *1. Proposal: Zero-Parameter `LazyConstant.of()` Overload:* Currently, the mandatory use of a factory method receiving a `Supplier` (due to the lack of a public constructor) can obscure the expressiveness of conditional or multiple-value initialization paths. **The Issue:** When looking at the declaration: LazyConstant foo = LazyConstant.of(() -> "hello"); the code gives the strong, immediate impression that the value is *always* initialized to "hello". This makes it difficult to infer that the constant might ultimately resolve to an alternative value set later via orElse() or another conditional path, especially when skimming the code: LazyConstant foo = LazyConstant.of(() -> "hello"); // When skimming the code it's not always obvious that this may not be the actual value void main() { if (someCondition()) { foo.get(); // Trigger initialization to "hello" } // If someCondition is false, the final value of foo is determined here: var res1 = foo.orElse("hello2"); // ... } *My Suggestion:* I propose introducing a *zero-parameter overloaded static factory method* of(): LazyConstant foo = LazyConstant.of(); This form explicitly communicates that the constant is initialized to an *unresolved* state, suggesting that the value will be determined downstream by the first invocation of an initialization/computation method. LazyConstant foo = LazyConstant.of(); // Clearly unresolved void main() { if (someCondition()) { foo.orElse("hello"); } var res1 = foo.orElse("hello2"); // ... } This is specially useful for clarity when one has conditional initialization in places such as the constructor of a class. For example private class Bar{ LazyConstant foo = LazyConstant.of(); private Bar(Some some){ if(some.condition()){ foo.orElse("foo"); } foo.orElse("foo2"); } String computeValue() { return "hello"; } String computeValue2(){ return "hello2"; } } 2. Method Naming Suggestion and and supplier in instance method for consistency in the API My second, much more minor observation relates to the instance method orElse(T t). While orElse fits a retrieval pattern, I personally feel that *compute* or *computeIfAbsent* would better express the intent of this method, as its primary function is not just to retrieve, but to trigger the computation and *set the final value* of the constant if it is currently uninitialized. Also, as the factory of() has a supplier i think this instance method should also receive a Supplier, This not only keeps the API consistent in the usage but makes more ergonomic the declaration of complex initialization logic inside the method. private class Bar{ LazyConstant foo = LazyConstant.of(InitParam::default); // Under the current API this is mandatory but in reality the value is set in the constructor, default is never really used. private Bar(Some some){ foo.compute(some::executeCallToCacheDBAndBringInitializationParams) //Real configuration happens here } } This last it's very common for initialization of configuration classes and singletons. Thank you so much for your attention, I hope you find this feedback useful. Always yours. David Grajales -------------- next part -------------- An HTML attachment was scrubbed... URL: From per-ake.minborg at oracle.com Fri Dec 5 11:05:49 2025 From: per-ake.minborg at oracle.com (Per-Ake Minborg) Date: Fri, 5 Dec 2025 11:05:49 +0000 Subject: Feedback about LazyConstants API (JEP526) In-Reply-To: References: Message-ID: Hi David, Thank you for trying out LazyConstant and providing feedback. That is precisely what previews are for! If you take a closer look at the specification of LazyConstant::orElse, it says that the method will never trigger initialization. And so, you can actually be sure that in your first example, foo is always initialized to "hello" (if ever initialized). It is only if foo is not initialized that the method will return "hello2" (again, without initializing foo). This is similar to how Optional works. It would be possible to entirely remove the orElse() method from the API, and in the rare cases where an equivalent functionality is called for, rely on LazyConstant::isInitialized instead. Best, Per Confidential- Oracle Internal ________________________________ From: amber-dev on behalf of david Grajales Sent: Friday, December 5, 2025 5:38 AM To: amber-dev ; core-libs-dev at openjdk.org Subject: Feedback about LazyConstants API (JEP526) Dear Java Dev Team, I am writing to provide feedback and two specific observations regarding the LazyConstant API, which is currently a preview feature in OpenJDK 26. I appreciate the API's direction and I think it's a good improvement compared to its first iteration; however, I see potential for improved expressiveness, particularly in conditional scenarios. 1. Proposal: Zero-Parameter `LazyConstant.of()` Overload: Currently, the mandatory use of a factory method receiving a `Supplier` (due to the lack of a public constructor) can obscure the expressiveness of conditional or multiple-value initialization paths. **The Issue:** When looking at the declaration: LazyConstant foo = LazyConstant.of(() -> "hello"); the code gives the strong, immediate impression that the value is always initialized to "hello". This makes it difficult to infer that the constant might ultimately resolve to an alternative value set later via orElse() or another conditional path, especially when skimming the code: LazyConstant foo = LazyConstant.of(() -> "hello"); // When skimming the code it's not always obvious that this may not be the actual value void main() { if (someCondition()) { foo.get(); // Trigger initialization to "hello" } // If someCondition is false, the final value of foo is determined here: var res1 = foo.orElse("hello2"); // ... } My Suggestion: I propose introducing a zero-parameter overloaded static factory method of(): LazyConstant foo = LazyConstant.of(); This form explicitly communicates that the constant is initialized to an unresolved state, suggesting that the value will be determined downstream by the first invocation of an initialization/computation method. LazyConstant foo = LazyConstant.of(); // Clearly unresolved void main() { if (someCondition()) { foo.orElse("hello"); } var res1 = foo.orElse("hello2"); // ... } This is specially useful for clarity when one has conditional initialization in places such as the constructor of a class. For example private class Bar{ LazyConstant foo = LazyConstant.of(); private Bar(Some some){ if(some.condition()){ foo.orElse("foo"); } foo.orElse("foo2"); } String computeValue() { return "hello"; } String computeValue2(){ return "hello2"; } } 2. Method Naming Suggestion and and supplier in instance method for consistency in the API My second, much more minor observation relates to the instance method orElse(T t). While orElse fits a retrieval pattern, I personally feel that compute or computeIfAbsent would better express the intent of this method, as its primary function is not just to retrieve, but to trigger the computation and set the final value of the constant if it is currently uninitialized. Also, as the factory of() has a supplier i think this instance method should also receive a Supplier, This not only keeps the API consistent in the usage but makes more ergonomic the declaration of complex initialization logic inside the method. private class Bar{ LazyConstant foo = LazyConstant.of(InitParam::default); // Under the current API this is mandatory but in reality the value is set in the constructor, default is never really used. private Bar(Some some){ foo.compute(some::executeCallToCacheDBAndBringInitializationParams) //Real configuration happens here } } This last it's very common for initialization of configuration classes and singletons. Thank you so much for your attention, I hope you find this feedback useful. Always yours. David Grajales -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.1993grajales at gmail.com Fri Dec 5 12:54:39 2025 From: david.1993grajales at gmail.com (david Grajales) Date: Fri, 5 Dec 2025 07:54:39 -0500 Subject: Feedback about LazyConstants API (JEP526) In-Reply-To: References: Message-ID: HI Per. I pleasure to talk with you. You are right about one thing but this actually makes the API less intuitive and harder to read and reason about. LazyConstant foo = LazyConstant.of(() -> "hello"); void main() { if (someCondition()) {// asume false foo.get(); } foo.orElse("hello2"); // ... println(foo.get()); // This prints "hello" } But if one assigns foo.orElse("hello2") to a variable, the variable actually gets the "hello2" value. void main() { if (someCondition()) {// asume false foo.get(); } var res = foo.orElse("hello2"); // ... var res2 = foo.orElse("hello3"); println(res); // This prints "hello2" println(res2);//This prints "hello3" } This is actually even more confusing and makes the API more error prone. I personally think once initialized the lazy constant should always return the same value (maybe through the .get() method only), and there should not be any possibility of getting a different values from the same instance either in the .of() static method or in any hypothetical instance method for conditional downstream logic. I guess one could achieve the latter with the static factory method through something like this (although less elegant) private class Bar{ private final LazyConstant foo; private Bar(Some some){ if(some.condition){ foo = LazyConstant.of(() -> "hello"); }else { foo = LazyConstant.of(() -> "hello2"); } } } Thank you for reading. This is all I have to report. Best regards. El vie, 5 dic 2025 a la(s) 6:05?a.m., Per-Ake Minborg ( per-ake.minborg at oracle.com) escribi?: > Hi David, > > Thank you for trying out LazyConstant and providing feedback. That is > precisely what previews are for! > > If you take a closer look at the specification of LazyConstant::orElse, it > says that the method will *never trigger initialization.* And so, you > *can* actually be sure that in your first example, foo is always > initialized to "hello" (if ever initialized). It is only if foo is not > initialized that the method will return "hello2" (again, without > initializing foo). This is similar to how Optional works. > > It would be possible to entirely remove the orElse() method from the API, > and in the rare cases where an equivalent functionality is called for, rely > on LazyConstant::isInitialized instead. > > Best, Per > > > Confidential- Oracle Internal > ------------------------------ > *From:* amber-dev on behalf of david > Grajales > *Sent:* Friday, December 5, 2025 5:38 AM > *To:* amber-dev ; core-libs-dev at openjdk.org < > core-libs-dev at openjdk.org> > *Subject:* Feedback about LazyConstants API (JEP526) > > Dear Java Dev Team, > > I am writing to provide feedback and two specific observations regarding > the LazyConstant API, which is currently a preview feature in OpenJDK 26. > > I appreciate the API's direction and I think it's a good improvement > compared to its first iteration; however, I see potential for improved > expressiveness, particularly in conditional scenarios. > > > *1. Proposal: Zero-Parameter `LazyConstant.of()` Overload:* > > Currently, the mandatory use of a factory method receiving a `Supplier` > (due to the lack of a public constructor) can obscure the expressiveness of > conditional or multiple-value initialization paths. **The Issue:** When > looking at the declaration: > > LazyConstant foo = LazyConstant.of(() -> "hello"); > > the code gives the strong, immediate impression that the value is *always* > initialized to "hello". This makes it difficult to infer that the > constant might ultimately resolve to an alternative value set later via > orElse() or another conditional path, especially when skimming the code: > > LazyConstant foo = LazyConstant.of(() -> "hello"); // When > skimming the code it's not always obvious that this may not be the actual > value > > void main() { > if (someCondition()) { > foo.get(); // Trigger initialization to "hello" > } > // If someCondition is false, the final value of foo is determined here: > > var res1 = foo.orElse("hello2"); // ... > } > > *My Suggestion:* I propose introducing a *zero-parameter overloaded > static factory method* of(): > > LazyConstant foo = LazyConstant.of(); > > This form explicitly communicates that the constant is initialized to an * > unresolved* state, suggesting that the value will be determined > downstream by the first invocation of an initialization/computation method. > > LazyConstant foo = LazyConstant.of(); // Clearly unresolved > void main() { > if (someCondition()) { > foo.orElse("hello"); > } > var res1 = foo.orElse("hello2"); // ... > } > > This is specially useful for clarity when one has conditional > initialization in places such as the constructor of a class. For example > > private class Bar{ > LazyConstant foo = LazyConstant.of(); > private Bar(Some some){ > if(some.condition()){ > foo.orElse("foo"); > } > foo.orElse("foo2"); > } > > String computeValue() { > return "hello"; > } > > String computeValue2(){ > return "hello2"; > } > } > 2. Method Naming Suggestion and and supplier in instance method for > consistency in the API > > My second, much more minor observation relates to the instance method orElse(T > t). > > While orElse fits a retrieval pattern, I personally feel that * compute* > or *computeIfAbsent* would better express the intent of this method, as > its primary function is not just to retrieve, but to trigger the > computation and *set the final value* of the constant if it is currently > uninitialized. Also, as the factory of() has a supplier i think this > instance method should also receive a Supplier, This not only keeps the API > consistent in the usage but makes more ergonomic the declaration of complex > initialization logic inside the method. > > > private class Bar{ > LazyConstant foo = LazyConstant.of(InitParam::default); // > Under the current API this is mandatory but in reality the value is set in > the constructor, default is never really used. > private Bar(Some some){ > foo.compute(some::executeCallToCacheDBAndBringInitializationParams) > //Real configuration happens here > > } > } > > This last it's very common for initialization of configuration classes and > singletons. > > > Thank you so much for your attention, I hope you find this feedback useful. > > Always yours. David Grajales > -------------- next part -------------- An HTML attachment was scrubbed... URL: From redio.development at gmail.com Fri Dec 5 13:31:56 2025 From: redio.development at gmail.com (Red IO) Date: Fri, 5 Dec 2025 14:31:56 +0100 Subject: Feedback about LazyConstants API (JEP526) In-Reply-To: References: Message-ID: Hi David, As par already said the orElse method doesn't initializes the LazyConstant. It just checks rather the value is init and if not calls the supplier to get a substitute for the missing constant. Example: LazyConstant x = LazyConstant.of(() -> "Const"); var uninit1 = x.orElse(() -> "substitute 1"); var uninit2 = x.orElse(() -> "substitute 2"); var init1 = x.get(); var init2 = x.orElse(() -> "substitute 3"); uninit1 and uninit2 get the substitute 1/2 And init1 and init2 get Const. This is surprising if you expect it to be a way to init it with an alternative value. My suggestion would to make the separation clear and allow for another use case by spliting this api in 2 parts: One class LazyConstant Takes a Supplier in static factory and exposes get() And Class LazyInit Which takes no arguments in the static factory and takes a supplier in the get method that gets called when get is called for the first time. In this case the source for the constant can be any piece of code that has access to the LazyConstant. This might be desired in some cases. In cases where it's not the other version can be used. This split makes it clear from which context the constant is initialized from (consumer or at declaration) Mixing those 2 or having methods that appear to do this is rather confusing. One solution for the "i might not want to init the constant" case the "orElse" method is meant to be is to have a method "tryGet" which returns Optional instead. This makes it clear that the value might not be there and is not initialized when calling the method. Nobody expects to init the constant when calling orElse on a returned Optional. My 2 suggestions here are completely independent and should be viewed as such. Great regards RedIODev On Fri, Dec 5, 2025, 13:55 david Grajales wrote: > HI Per. I pleasure to talk with you. > > You are right about one thing but this actually makes the API less > intuitive and harder to read and reason about. > > LazyConstant foo = LazyConstant.of(() -> "hello"); > > void main() { > if (someCondition()) {// asume false > foo.get(); > } > foo.orElse("hello2"); // ... > > println(foo.get()); // This prints "hello" > } > > But if one assigns foo.orElse("hello2") to a variable, the variable > actually gets the "hello2" value. > > void main() { > if (someCondition()) {// asume false > foo.get(); > } > var res = foo.orElse("hello2"); // ... > var res2 = foo.orElse("hello3"); > println(res); // This prints "hello2" > println(res2);//This prints "hello3" > } > > This is actually even more confusing and makes the API more error prone. I > personally think once initialized the lazy constant should always return > the same value (maybe through the .get() method only), and there should not > be any possibility of getting a different values from the same instance > either in the .of() static method or in any hypothetical instance method > for conditional downstream logic. I guess one could achieve the latter > with the static factory method through something like this (although less > elegant) > > private class Bar{ > private final LazyConstant foo; > private Bar(Some some){ > > if(some.condition){ > foo = LazyConstant.of(() -> "hello"); > }else { > foo = LazyConstant.of(() -> "hello2"); > } > } > } > > Thank you for reading. This is all I have to report. > > Best regards. > > > > El vie, 5 dic 2025 a la(s) 6:05?a.m., Per-Ake Minborg ( > per-ake.minborg at oracle.com) escribi?: > >> Hi David, >> >> Thank you for trying out LazyConstant and providing feedback. That is >> precisely what previews are for! >> >> If you take a closer look at the specification of LazyConstant::orElse, it >> says that the method will *never trigger initialization.* And so, you >> *can* actually be sure that in your first example, foo is always >> initialized to "hello" (if ever initialized). It is only if foo is not >> initialized that the method will return "hello2" (again, without >> initializing foo). This is similar to how Optional works. >> >> It would be possible to entirely remove the orElse() method from the >> API, and in the rare cases where an equivalent functionality is called for, >> rely on LazyConstant::isInitialized instead. >> >> Best, Per >> >> >> Confidential- Oracle Internal >> ------------------------------ >> *From:* amber-dev on behalf of david >> Grajales >> *Sent:* Friday, December 5, 2025 5:38 AM >> *To:* amber-dev ; core-libs-dev at openjdk.org < >> core-libs-dev at openjdk.org> >> *Subject:* Feedback about LazyConstants API (JEP526) >> >> Dear Java Dev Team, >> >> I am writing to provide feedback and two specific observations regarding >> the LazyConstant API, which is currently a preview feature in OpenJDK 26. >> >> I appreciate the API's direction and I think it's a good improvement >> compared to its first iteration; however, I see potential for improved >> expressiveness, particularly in conditional scenarios. >> >> >> *1. Proposal: Zero-Parameter `LazyConstant.of()` Overload:* >> >> Currently, the mandatory use of a factory method receiving a `Supplier` >> (due to the lack of a public constructor) can obscure the expressiveness of >> conditional or multiple-value initialization paths. **The Issue:** When >> looking at the declaration: >> >> LazyConstant foo = LazyConstant.of(() -> "hello"); >> >> the code gives the strong, immediate impression that the value is >> *always* initialized to "hello". This makes it difficult to infer that >> the constant might ultimately resolve to an alternative value set later via >> orElse() or another conditional path, especially when skimming the code: >> >> LazyConstant foo = LazyConstant.of(() -> "hello"); // When >> skimming the code it's not always obvious that this may not be the actual >> value >> >> void main() { >> if (someCondition()) { >> foo.get(); // Trigger initialization to "hello" >> } >> // If someCondition is false, the final value of foo is determined >> here: >> var res1 = foo.orElse("hello2"); // ... >> } >> >> *My Suggestion:* I propose introducing a *zero-parameter overloaded >> static factory method* of(): >> >> LazyConstant foo = LazyConstant.of(); >> >> This form explicitly communicates that the constant is initialized to an * >> unresolved* state, suggesting that the value will be determined >> downstream by the first invocation of an initialization/computation method. >> >> LazyConstant foo = LazyConstant.of(); // Clearly unresolved >> void main() { >> if (someCondition()) { >> foo.orElse("hello"); >> } >> var res1 = foo.orElse("hello2"); // ... >> } >> >> This is specially useful for clarity when one has conditional >> initialization in places such as the constructor of a class. For example >> >> private class Bar{ >> LazyConstant foo = LazyConstant.of(); >> private Bar(Some some){ >> if(some.condition()){ >> foo.orElse("foo"); >> } >> foo.orElse("foo2"); >> } >> >> String computeValue() { >> return "hello"; >> } >> >> String computeValue2(){ >> return "hello2"; >> } >> } >> 2. Method Naming Suggestion and and supplier in instance method for >> consistency in the API >> >> My second, much more minor observation relates to the instance method orElse(T >> t). >> >> While orElse fits a retrieval pattern, I personally feel that * compute* >> or *computeIfAbsent* would better express the intent of this method, as >> its primary function is not just to retrieve, but to trigger the >> computation and *set the final value* of the constant if it is currently >> uninitialized. Also, as the factory of() has a supplier i think this >> instance method should also receive a Supplier, This not only keeps the API >> consistent in the usage but makes more ergonomic the declaration of complex >> initialization logic inside the method. >> >> >> private class Bar{ >> LazyConstant foo = LazyConstant.of(InitParam::default); >> // Under the current API this is mandatory but in reality the value is set >> in the constructor, default is never really used. >> private Bar(Some some){ >> >> foo.compute(some::executeCallToCacheDBAndBringInitializationParams) //Real >> configuration happens here >> >> } >> } >> >> This last it's very common for initialization of configuration classes >> and singletons. >> >> >> Thank you so much for your attention, I hope you find this feedback >> useful. >> >> Always yours. David Grajales >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From burnytc at gmail.com Fri Dec 5 13:50:46 2025 From: burnytc at gmail.com (Kirill Semyonkin) Date: Fri, 5 Dec 2025 16:50:46 +0300 Subject: Feedback about LazyConstants API (JEP526) In-Reply-To: References: Message-ID: My guess is that David is understanding what is possible with the current API, but asking for that to actually not be an option - thus getting rid of not only orElse, but also isInitialized at all, making LazyConstant API to do only one thing: represent a value that will (not may, will) be computed whenever you query it. I guess this stems from the new naming for it, Lazy + Constant implies that there would be only one constant value received from the API (not Optional.empty(), not some orElse's default), which will always just be computed at a later point (lazy). This was not implied with StableValue naming, which sounds to me like it's implying that the value will be "stabilized" at some point. I guess the current LazyConstants API is too nice/not low level enough (it has neat things like orElse) to suggest that there could be such an access like isInitialized (which is low level, the whole API that has to do with it needs to "scare" the developers so that they would pay more attention when using it to not make logical mistakes). So I would agree with Red if it would be split into two different APIs, where my variant is StableValue (StabilizableValue?) suggesting that they provide some value which can be stabilized (and if it isn't yet, you can check that), and LazyConstant based on that, which would always lazily compute some particular constant value, and not do anything else besides that responsibility; and Red's variant is that the second class (or maybe have all of these, making it the third class?) would accept suppliers always. - Kirill Semyonkin ??, 5 ???. 2025??. ? 16:32, Red IO : > Hi David, > As par already said the orElse method doesn't initializes the > LazyConstant. > It just checks rather the value is init and if not calls the supplier to > get a substitute for the missing constant. > Example: > LazyConstant x = LazyConstant.of(() -> "Const"); > var uninit1 = x.orElse(() -> "substitute 1"); > var uninit2 = x.orElse(() -> "substitute 2"); > var init1 = x.get(); > var init2 = x.orElse(() -> "substitute 3"); > uninit1 and uninit2 get the substitute 1/2 > And init1 and init2 get Const. > > This is surprising if you expect it to be a way to init it with an > alternative value. > > My suggestion would to make the separation clear and allow for another use > case by spliting this api in 2 parts: > One class LazyConstant > Takes a Supplier in static factory and exposes get() > > And > Class LazyInit > Which takes no arguments in the static factory and takes a supplier in the > get method that gets called when get is called for the first time. > In this case the source for the constant can be any piece of code that has > access to the LazyConstant. This might be desired in some cases. In cases > where it's not the other version can be used. > > This split makes it clear from which context the constant is initialized > from (consumer or at declaration) > > Mixing those 2 or having methods that appear to do this is rather > confusing. > > > > One solution for the "i might not want to init the constant" case the > "orElse" method is meant to be is to have a method "tryGet" which returns > Optional instead. This makes it clear that the value might not be there and > is not initialized when calling the method. Nobody expects to init the > constant when calling orElse on a returned Optional. > > My 2 suggestions here are completely independent and should be viewed as > such. > > Great regards > RedIODev > > On Fri, Dec 5, 2025, 13:55 david Grajales > wrote: > >> HI Per. I pleasure to talk with you. >> >> You are right about one thing but this actually makes the API less >> intuitive and harder to read and reason about. >> >> LazyConstant foo = LazyConstant.of(() -> "hello"); >> >> void main() { >> if (someCondition()) {// asume false >> foo.get(); >> } >> foo.orElse("hello2"); // ... >> >> println(foo.get()); // This prints "hello" >> } >> >> But if one assigns foo.orElse("hello2") to a variable, the variable >> actually gets the "hello2" value. >> >> void main() { >> if (someCondition()) {// asume false >> foo.get(); >> } >> var res = foo.orElse("hello2"); // ... >> var res2 = foo.orElse("hello3"); >> println(res); // This prints "hello2" >> println(res2);//This prints "hello3" >> } >> >> This is actually even more confusing and makes the API more error prone. >> I personally think once initialized the lazy constant should always return >> the same value (maybe through the .get() method only), and there should not >> be any possibility of getting a different values from the same instance >> either in the .of() static method or in any hypothetical instance method >> for conditional downstream logic. I guess one could achieve the latter >> with the static factory method through something like this (although less >> elegant) >> >> private class Bar{ >> private final LazyConstant foo; >> private Bar(Some some){ >> >> if(some.condition){ >> foo = LazyConstant.of(() -> "hello"); >> }else { >> foo = LazyConstant.of(() -> "hello2"); >> } >> } >> } >> >> Thank you for reading. This is all I have to report. >> >> Best regards. >> >> >> >> El vie, 5 dic 2025 a la(s) 6:05?a.m., Per-Ake Minborg ( >> per-ake.minborg at oracle.com) escribi?: >> >>> Hi David, >>> >>> Thank you for trying out LazyConstant and providing feedback. That is >>> precisely what previews are for! >>> >>> If you take a closer look at the specification of LazyConstant::orElse, it >>> says that the method will *never trigger initialization.* And so, you >>> *can* actually be sure that in your first example, foo is always >>> initialized to "hello" (if ever initialized). It is only if foo is not >>> initialized that the method will return "hello2" (again, without >>> initializing foo). This is similar to how Optional works. >>> >>> It would be possible to entirely remove the orElse() method from the >>> API, and in the rare cases where an equivalent functionality is called for, >>> rely on LazyConstant::isInitialized instead. >>> >>> Best, Per >>> >>> >>> Confidential- Oracle Internal >>> ------------------------------ >>> *From:* amber-dev on behalf of david >>> Grajales >>> *Sent:* Friday, December 5, 2025 5:38 AM >>> *To:* amber-dev ; core-libs-dev at openjdk.org < >>> core-libs-dev at openjdk.org> >>> *Subject:* Feedback about LazyConstants API (JEP526) >>> >>> Dear Java Dev Team, >>> >>> I am writing to provide feedback and two specific observations >>> regarding the LazyConstant API, which is currently a preview feature in >>> OpenJDK 26. >>> >>> I appreciate the API's direction and I think it's a good improvement >>> compared to its first iteration; however, I see potential for improved >>> expressiveness, particularly in conditional scenarios. >>> >>> >>> *1. Proposal: Zero-Parameter `LazyConstant.of()` Overload:* >>> >>> Currently, the mandatory use of a factory method receiving a `Supplier` >>> (due to the lack of a public constructor) can obscure the expressiveness of >>> conditional or multiple-value initialization paths. **The Issue:** When >>> looking at the declaration: >>> >>> LazyConstant foo = LazyConstant.of(() -> "hello"); >>> >>> the code gives the strong, immediate impression that the value is >>> *always* initialized to "hello". This makes it difficult to infer that >>> the constant might ultimately resolve to an alternative value set later via >>> orElse() or another conditional path, especially when skimming the code: >>> >>> LazyConstant foo = LazyConstant.of(() -> "hello"); // When >>> skimming the code it's not always obvious that this may not be the actual >>> value >>> >>> void main() { >>> if (someCondition()) { >>> foo.get(); // Trigger initialization to "hello" >>> } >>> // If someCondition is false, the final value of foo is determined >>> here: >>> var res1 = foo.orElse("hello2"); // ... >>> } >>> >>> *My Suggestion:* I propose introducing a *zero-parameter overloaded >>> static factory method* of(): >>> >>> LazyConstant foo = LazyConstant.of(); >>> >>> This form explicitly communicates that the constant is initialized to an * >>> unresolved* state, suggesting that the value will be determined >>> downstream by the first invocation of an initialization/computation method. >>> >>> LazyConstant foo = LazyConstant.of(); // Clearly unresolved >>> void main() { >>> if (someCondition()) { >>> foo.orElse("hello"); >>> } >>> var res1 = foo.orElse("hello2"); // ... >>> } >>> >>> This is specially useful for clarity when one has conditional >>> initialization in places such as the constructor of a class. For example >>> >>> private class Bar{ >>> LazyConstant foo = LazyConstant.of(); >>> private Bar(Some some){ >>> if(some.condition()){ >>> foo.orElse("foo"); >>> } >>> foo.orElse("foo2"); >>> } >>> >>> String computeValue() { >>> return "hello"; >>> } >>> >>> String computeValue2(){ >>> return "hello2"; >>> } >>> } >>> 2. Method Naming Suggestion and and supplier in instance method for >>> consistency in the API >>> >>> My second, much more minor observation relates to the instance method orElse(T >>> t). >>> >>> While orElse fits a retrieval pattern, I personally feel that * compute* >>> or *computeIfAbsent* would better express the intent of this method, as >>> its primary function is not just to retrieve, but to trigger the >>> computation and *set the final value* of the constant if it is >>> currently uninitialized. Also, as the factory of() has a supplier i think >>> this instance method should also receive a Supplier, This not only keeps >>> the API consistent in the usage but makes more ergonomic the declaration of >>> complex initialization logic inside the method. >>> >>> >>> private class Bar{ >>> LazyConstant foo = LazyConstant.of(InitParam::default); >>> // Under the current API this is mandatory but in reality the value is set >>> in the constructor, default is never really used. >>> private Bar(Some some){ >>> >>> foo.compute(some::executeCallToCacheDBAndBringInitializationParams) //Real >>> configuration happens here >>> >>> } >>> } >>> >>> This last it's very common for initialization of configuration classes >>> and singletons. >>> >>> >>> Thank you so much for your attention, I hope you find this feedback >>> useful. >>> >>> Always yours. David Grajales >>> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From davidalayachew at gmail.com Fri Dec 5 13:51:46 2025 From: davidalayachew at gmail.com (David Alayachew) Date: Fri, 5 Dec 2025 08:51:46 -0500 Subject: Feedback about LazyConstants API (JEP526) In-Reply-To: References: Message-ID: Caveat -- I have only used the Java 25 version of this library. I agree that the name orElse() is not intuitive. It was made more intuitive by the existence of orElseSet(). In its absence, changing the name makes sense. Though, I'm definitely open to just removing the method. This is easy enough to accomplish ourselves. Would prefer a rename though. On Fri, Dec 5, 2025, 8:32?AM Red IO wrote: > Hi David, > As par already said the orElse method doesn't initializes the > LazyConstant. > It just checks rather the value is init and if not calls the supplier to > get a substitute for the missing constant. > Example: > LazyConstant x = LazyConstant.of(() -> "Const"); > var uninit1 = x.orElse(() -> "substitute 1"); > var uninit2 = x.orElse(() -> "substitute 2"); > var init1 = x.get(); > var init2 = x.orElse(() -> "substitute 3"); > uninit1 and uninit2 get the substitute 1/2 > And init1 and init2 get Const. > > This is surprising if you expect it to be a way to init it with an > alternative value. > > My suggestion would to make the separation clear and allow for another use > case by spliting this api in 2 parts: > One class LazyConstant > Takes a Supplier in static factory and exposes get() > > And > Class LazyInit > Which takes no arguments in the static factory and takes a supplier in the > get method that gets called when get is called for the first time. > In this case the source for the constant can be any piece of code that has > access to the LazyConstant. This might be desired in some cases. In cases > where it's not the other version can be used. > > This split makes it clear from which context the constant is initialized > from (consumer or at declaration) > > Mixing those 2 or having methods that appear to do this is rather > confusing. > > > > One solution for the "i might not want to init the constant" case the > "orElse" method is meant to be is to have a method "tryGet" which returns > Optional instead. This makes it clear that the value might not be there and > is not initialized when calling the method. Nobody expects to init the > constant when calling orElse on a returned Optional. > > My 2 suggestions here are completely independent and should be viewed as > such. > > Great regards > RedIODev > > On Fri, Dec 5, 2025, 13:55 david Grajales > wrote: > >> HI Per. I pleasure to talk with you. >> >> You are right about one thing but this actually makes the API less >> intuitive and harder to read and reason about. >> >> LazyConstant foo = LazyConstant.of(() -> "hello"); >> >> void main() { >> if (someCondition()) {// asume false >> foo.get(); >> } >> foo.orElse("hello2"); // ... >> >> println(foo.get()); // This prints "hello" >> } >> >> But if one assigns foo.orElse("hello2") to a variable, the variable >> actually gets the "hello2" value. >> >> void main() { >> if (someCondition()) {// asume false >> foo.get(); >> } >> var res = foo.orElse("hello2"); // ... >> var res2 = foo.orElse("hello3"); >> println(res); // This prints "hello2" >> println(res2);//This prints "hello3" >> } >> >> This is actually even more confusing and makes the API more error prone. >> I personally think once initialized the lazy constant should always return >> the same value (maybe through the .get() method only), and there should not >> be any possibility of getting a different values from the same instance >> either in the .of() static method or in any hypothetical instance method >> for conditional downstream logic. I guess one could achieve the latter >> with the static factory method through something like this (although less >> elegant) >> >> private class Bar{ >> private final LazyConstant foo; >> private Bar(Some some){ >> >> if(some.condition){ >> foo = LazyConstant.of(() -> "hello"); >> }else { >> foo = LazyConstant.of(() -> "hello2"); >> } >> } >> } >> >> Thank you for reading. This is all I have to report. >> >> Best regards. >> >> >> >> El vie, 5 dic 2025 a la(s) 6:05?a.m., Per-Ake Minborg ( >> per-ake.minborg at oracle.com) escribi?: >> >>> Hi David, >>> >>> Thank you for trying out LazyConstant and providing feedback. That is >>> precisely what previews are for! >>> >>> If you take a closer look at the specification of LazyConstant::orElse, it >>> says that the method will *never trigger initialization.* And so, you >>> *can* actually be sure that in your first example, foo is always >>> initialized to "hello" (if ever initialized). It is only if foo is not >>> initialized that the method will return "hello2" (again, without >>> initializing foo). This is similar to how Optional works. >>> >>> It would be possible to entirely remove the orElse() method from the >>> API, and in the rare cases where an equivalent functionality is called for, >>> rely on LazyConstant::isInitialized instead. >>> >>> Best, Per >>> >>> >>> Confidential- Oracle Internal >>> ------------------------------ >>> *From:* amber-dev on behalf of david >>> Grajales >>> *Sent:* Friday, December 5, 2025 5:38 AM >>> *To:* amber-dev ; core-libs-dev at openjdk.org < >>> core-libs-dev at openjdk.org> >>> *Subject:* Feedback about LazyConstants API (JEP526) >>> >>> Dear Java Dev Team, >>> >>> I am writing to provide feedback and two specific observations >>> regarding the LazyConstant API, which is currently a preview feature in >>> OpenJDK 26. >>> >>> I appreciate the API's direction and I think it's a good improvement >>> compared to its first iteration; however, I see potential for improved >>> expressiveness, particularly in conditional scenarios. >>> >>> >>> *1. Proposal: Zero-Parameter `LazyConstant.of()` Overload:* >>> >>> Currently, the mandatory use of a factory method receiving a `Supplier` >>> (due to the lack of a public constructor) can obscure the expressiveness of >>> conditional or multiple-value initialization paths. **The Issue:** When >>> looking at the declaration: >>> >>> LazyConstant foo = LazyConstant.of(() -> "hello"); >>> >>> the code gives the strong, immediate impression that the value is >>> *always* initialized to "hello". This makes it difficult to infer that >>> the constant might ultimately resolve to an alternative value set later via >>> orElse() or another conditional path, especially when skimming the code: >>> >>> LazyConstant foo = LazyConstant.of(() -> "hello"); // When >>> skimming the code it's not always obvious that this may not be the actual >>> value >>> >>> void main() { >>> if (someCondition()) { >>> foo.get(); // Trigger initialization to "hello" >>> } >>> // If someCondition is false, the final value of foo is determined >>> here: >>> var res1 = foo.orElse("hello2"); // ... >>> } >>> >>> *My Suggestion:* I propose introducing a *zero-parameter overloaded >>> static factory method* of(): >>> >>> LazyConstant foo = LazyConstant.of(); >>> >>> This form explicitly communicates that the constant is initialized to an * >>> unresolved* state, suggesting that the value will be determined >>> downstream by the first invocation of an initialization/computation method. >>> >>> LazyConstant foo = LazyConstant.of(); // Clearly unresolved >>> void main() { >>> if (someCondition()) { >>> foo.orElse("hello"); >>> } >>> var res1 = foo.orElse("hello2"); // ... >>> } >>> >>> This is specially useful for clarity when one has conditional >>> initialization in places such as the constructor of a class. For example >>> >>> private class Bar{ >>> LazyConstant foo = LazyConstant.of(); >>> private Bar(Some some){ >>> if(some.condition()){ >>> foo.orElse("foo"); >>> } >>> foo.orElse("foo2"); >>> } >>> >>> String computeValue() { >>> return "hello"; >>> } >>> >>> String computeValue2(){ >>> return "hello2"; >>> } >>> } >>> 2. Method Naming Suggestion and and supplier in instance method for >>> consistency in the API >>> >>> My second, much more minor observation relates to the instance method orElse(T >>> t). >>> >>> While orElse fits a retrieval pattern, I personally feel that * compute* >>> or *computeIfAbsent* would better express the intent of this method, as >>> its primary function is not just to retrieve, but to trigger the >>> computation and *set the final value* of the constant if it is >>> currently uninitialized. Also, as the factory of() has a supplier i think >>> this instance method should also receive a Supplier, This not only keeps >>> the API consistent in the usage but makes more ergonomic the declaration of >>> complex initialization logic inside the method. >>> >>> >>> private class Bar{ >>> LazyConstant foo = LazyConstant.of(InitParam::default); >>> // Under the current API this is mandatory but in reality the value is set >>> in the constructor, default is never really used. >>> private Bar(Some some){ >>> >>> foo.compute(some::executeCallToCacheDBAndBringInitializationParams) //Real >>> configuration happens here >>> >>> } >>> } >>> >>> This last it's very common for initialization of configuration classes >>> and singletons. >>> >>> >>> Thank you so much for your attention, I hope you find this feedback >>> useful. >>> >>> Always yours. David Grajales >>> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From per-ake.minborg at oracle.com Mon Dec 8 12:31:37 2025 From: per-ake.minborg at oracle.com (Per-Ake Minborg) Date: Mon, 8 Dec 2025 12:31:37 +0000 Subject: [External] : Re: Feedback about LazyConstants API (JEP526) In-Reply-To: References: Message-ID: So, it is nice that folks seem to agree that LazyConstant should only compute and initialize its contents from the Supplier/lambda given at declaration time. The orElse method seems to blur the contours of LazyConstant , and so, as previously said, we might consider removing the method altogether in the next preview. It is also a fact that many have identified a need for "something else more low-level" that supports a more imperative programming model when working with constants that are lazily set. We do not rule out that such a thing might appear in a future JDK version. Best, Per Confidential- Oracle Internal ________________________________ From: David Alayachew Sent: Friday, December 5, 2025 2:51 PM To: Red IO Cc: david Grajales ; Per-Ake Minborg ; amber-dev ; core-libs-dev Subject: [External] : Re: Feedback about LazyConstants API (JEP526) Caveat -- I have only used the Java 25 version of this library. I agree that the name orElse() is not intuitive. It was made more intuitive by the existence of orElseSet(). In its absence, changing the name makes sense. Though, I'm definitely open to just removing the method. This is easy enough to accomplish ourselves. Would prefer a rename though. On Fri, Dec 5, 2025, 8:32?AM Red IO > wrote: Hi David, As par already said the orElse method doesn't initializes the LazyConstant. It just checks rather the value is init and if not calls the supplier to get a substitute for the missing constant. Example: LazyConstant x = LazyConstant.of(() -> "Const"); var uninit1 = x.orElse(() -> "substitute 1"); var uninit2 = x.orElse(() -> "substitute 2"); var init1 = x.get(); var init2 = x.orElse(() -> "substitute 3"); uninit1 and uninit2 get the substitute 1/2 And init1 and init2 get Const. This is surprising if you expect it to be a way to init it with an alternative value. My suggestion would to make the separation clear and allow for another use case by spliting this api in 2 parts: One class LazyConstant Takes a Supplier in static factory and exposes get() And Class LazyInit Which takes no arguments in the static factory and takes a supplier in the get method that gets called when get is called for the first time. In this case the source for the constant can be any piece of code that has access to the LazyConstant. This might be desired in some cases. In cases where it's not the other version can be used. This split makes it clear from which context the constant is initialized from (consumer or at declaration) Mixing those 2 or having methods that appear to do this is rather confusing. One solution for the "i might not want to init the constant" case the "orElse" method is meant to be is to have a method "tryGet" which returns Optional instead. This makes it clear that the value might not be there and is not initialized when calling the method. Nobody expects to init the constant when calling orElse on a returned Optional. My 2 suggestions here are completely independent and should be viewed as such. Great regards RedIODev On Fri, Dec 5, 2025, 13:55 david Grajales > wrote: HI Per. I pleasure to talk with you. You are right about one thing but this actually makes the API less intuitive and harder to read and reason about. LazyConstant foo = LazyConstant.of(() -> "hello"); void main() { if (someCondition()) {// asume false foo.get(); } foo.orElse("hello2"); // ... println(foo.get()); // This prints "hello" } But if one assigns foo.orElse("hello2") to a variable, the variable actually gets the "hello2" value. void main() { if (someCondition()) {// asume false foo.get(); } var res = foo.orElse("hello2"); // ... var res2 = foo.orElse("hello3"); println(res); // This prints "hello2" println(res2);//This prints "hello3" } This is actually even more confusing and makes the API more error prone. I personally think once initialized the lazy constant should always return the same value (maybe through the .get() method only), and there should not be any possibility of getting a different values from the same instance either in the .of() static method or in any hypothetical instance method for conditional downstream logic. I guess one could achieve the latter with the static factory method through something like this (although less elegant) private class Bar{ private final LazyConstant foo; private Bar(Some some){ if(some.condition){ foo = LazyConstant.of(() -> "hello"); }else { foo = LazyConstant.of(() -> "hello2"); } } } Thank you for reading. This is all I have to report. Best regards. El vie, 5 dic 2025 a la(s) 6:05?a.m., Per-Ake Minborg (per-ake.minborg at oracle.com) escribi?: Hi David, Thank you for trying out LazyConstant and providing feedback. That is precisely what previews are for! If you take a closer look at the specification of LazyConstant::orElse, it says that the method will never trigger initialization. And so, you can actually be sure that in your first example, foo is always initialized to "hello" (if ever initialized). It is only if foo is not initialized that the method will return "hello2" (again, without initializing foo). This is similar to how Optional works. It would be possible to entirely remove the orElse() method from the API, and in the rare cases where an equivalent functionality is called for, rely on LazyConstant::isInitialized instead. Best, Per Confidential- Oracle Internal ________________________________ From: amber-dev > on behalf of david Grajales > Sent: Friday, December 5, 2025 5:38 AM To: amber-dev >; core-libs-dev at openjdk.org > Subject: Feedback about LazyConstants API (JEP526) Dear Java Dev Team, I am writing to provide feedback and two specific observations regarding the LazyConstant API, which is currently a preview feature in OpenJDK 26. I appreciate the API's direction and I think it's a good improvement compared to its first iteration; however, I see potential for improved expressiveness, particularly in conditional scenarios. 1. Proposal: Zero-Parameter `LazyConstant.of()` Overload: Currently, the mandatory use of a factory method receiving a `Supplier` (due to the lack of a public constructor) can obscure the expressiveness of conditional or multiple-value initialization paths. **The Issue:** When looking at the declaration: LazyConstant foo = LazyConstant.of(() -> "hello"); the code gives the strong, immediate impression that the value is always initialized to "hello". This makes it difficult to infer that the constant might ultimately resolve to an alternative value set later via orElse() or another conditional path, especially when skimming the code: LazyConstant foo = LazyConstant.of(() -> "hello"); // When skimming the code it's not always obvious that this may not be the actual value void main() { if (someCondition()) { foo.get(); // Trigger initialization to "hello" } // If someCondition is false, the final value of foo is determined here: var res1 = foo.orElse("hello2"); // ... } My Suggestion: I propose introducing a zero-parameter overloaded static factory method of(): LazyConstant foo = LazyConstant.of(); This form explicitly communicates that the constant is initialized to an unresolved state, suggesting that the value will be determined downstream by the first invocation of an initialization/computation method. LazyConstant foo = LazyConstant.of(); // Clearly unresolved void main() { if (someCondition()) { foo.orElse("hello"); } var res1 = foo.orElse("hello2"); // ... } This is specially useful for clarity when one has conditional initialization in places such as the constructor of a class. For example private class Bar{ LazyConstant foo = LazyConstant.of(); private Bar(Some some){ if(some.condition()){ foo.orElse("foo"); } foo.orElse("foo2"); } String computeValue() { return "hello"; } String computeValue2(){ return "hello2"; } } 2. Method Naming Suggestion and and supplier in instance method for consistency in the API My second, much more minor observation relates to the instance method orElse(T t). While orElse fits a retrieval pattern, I personally feel that compute or computeIfAbsent would better express the intent of this method, as its primary function is not just to retrieve, but to trigger the computation and set the final value of the constant if it is currently uninitialized. Also, as the factory of() has a supplier i think this instance method should also receive a Supplier, This not only keeps the API consistent in the usage but makes more ergonomic the declaration of complex initialization logic inside the method. private class Bar{ LazyConstant foo = LazyConstant.of(InitParam::default); // Under the current API this is mandatory but in reality the value is set in the constructor, default is never really used. private Bar(Some some){ foo.compute(some::executeCallToCacheDBAndBringInitializationParams) //Real configuration happens here } } This last it's very common for initialization of configuration classes and singletons. Thank you so much for your attention, I hope you find this feedback useful. Always yours. David Grajales -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Tue Dec 9 10:25:26 2025 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Tue, 9 Dec 2025 10:25:26 +0000 Subject: [External] : Re: Feedback about LazyConstants API (JEP526) In-Reply-To: References: Message-ID: <10add665-86f5-4a5e-9b4b-d4ceb20b4494@oracle.com> I agree with most of the conclusions in this thread. One small nit is that, in reality, `orElse` is a "primitive" in disguise. E.g. you can implement `get` in terms of `orElse` but not the other way around (unless you are willing to do _two_ accessed to the underlying value). So, while we could drop it, we would also lose something (which is why we decided to keep it, at least for now). Maurizio On 08/12/2025 12:31, Per-Ake Minborg wrote: > So, it is nice that folks seem to agree that |LazyConstant|?should > only compute and initialize its contents from the Supplier/lambda > given at declaration time. The |orElse|?method seems to blur the > contours of |LazyConstant|?, and so, as previously said, we might > consider removing the method altogether in the next preview. > > It is also a fact that many have identified a need for "something else > more low-level" that supports a more imperative programming model when > working with constants that are lazily set. We do not rule out that > such a thing might appear in a future JDK version. > > Best, Per > > Confidential- Oracle Internal > ------------------------------------------------------------------------ > *From:* David Alayachew > *Sent:* Friday, December 5, 2025 2:51 PM > *To:* Red IO > *Cc:* david Grajales ; Per-Ake Minborg > ; amber-dev ; > core-libs-dev > *Subject:* [External] : Re: Feedback about LazyConstants API (JEP526) > Caveat -- I have only used the Java 25 version of this library. > > I agree that the name orElse() is not intuitive. It was made more > intuitive by the existence of orElseSet(). In its absence, changing > the name makes sense. > > Though, I'm definitely open to just removing the method. This is easy > enough to accomplish ourselves. Would prefer a rename though. > > On Fri, Dec 5, 2025, 8:32?AM Red IO wrote: > > Hi David, > As par already said the orElse method doesn't initializes the > LazyConstant. > It just checks rather the value is init and if not calls the > supplier to get a substitute for the missing constant. > Example: > LazyConstant x = LazyConstant.of(() -> "Const"); > var uninit1 = x.orElse(() -> "substitute 1"); > var uninit2 = x.orElse(() -> "substitute 2"); > var init1 = x.get(); > var init2 = x.orElse(() -> "substitute 3"); > uninit1 and uninit2 get the substitute 1/2 > And init1 and init2 get Const. > > This is surprising if you expect it to be a way to init it with an > alternative value. > > My suggestion would to make the separation clear and allow for > another use case by spliting this api in 2 parts: > One class LazyConstant > Takes a Supplier in static factory and exposes get() > > And > Class LazyInit > Which takes no arguments in the static factory and takes a > supplier in the get method that gets called when get is called for > the first time. > In this case the source for the constant can be any piece of code > that has access to the LazyConstant. This might be desired in some > cases. In cases where it's not the other version can be used. > > This split makes it clear from which context the constant is > initialized from (consumer or at declaration) > > Mixing those 2 or having methods that appear to do this is rather > confusing. > > > > One solution for the "i might not want to init the constant" case > the "orElse" method is meant to be is to have a method "tryGet" > which returns Optional instead. This makes it clear that the value > might not be there and is not initialized when calling the method. > Nobody expects to init the constant when calling orElse on a > returned Optional. > > My 2 suggestions here are completely independent and should be > viewed as such. > > Great regards > RedIODev > > On Fri, Dec 5, 2025, 13:55 david Grajales > wrote: > > HI Per. I pleasure to talk with you. > > You are right about one thing but this actually makes the API > less intuitive and harder to read and reason about. > > LazyConstant foo = LazyConstant.of(() -> "hello"); > > void main() { > ? ? if (someCondition()) {// asume false > ? ? ? ? foo.get(); > ? ? } > ? ? foo.orElse("hello2"); // ... > > ? ? println(foo.get()); // This prints "hello" > } > > But if one assigns foo.orElse("hello2") to a variable, the > variable actually gets the "hello2" value. > > void main() { > ? ? if (someCondition()) {// asume false > ? ? ? ? foo.get(); > ? ? } > ? ? var res = foo.orElse("hello2"); // ... > ? ? var res2 = foo.orElse("hello3"); > ? ? println(res); // This prints "hello2" > ? ? println(res2);//This prints "hello3" > } > > This is actually even more confusing and makes the API more > error prone. I personally think once initialized the lazy > constant should always return the same value (maybe through > the .get() method only), and there should not be any > possibility of getting a different values?from the same > instance either in the .of() static method or in any > hypothetical instance method for conditional downstream > logic.? I guess one could achieve the latter with the static > factory method through?something like this (although?less elegant) > > private class Bar{ > ? ? private final LazyConstant foo; > ? ? private Bar(Some some){ > > ? ? ? ? if(some.condition){ > ? ? ? ? ? ? foo = LazyConstant.of(() -> "hello"); > ? ? ? ? }else { > ? ? ? ? ? ? foo = LazyConstant.of(() -> "hello2"); > ? ? ? ? } > ? ? } > } > > Thank you for reading. This is all I have to report. > > Best regards. > > > > El vie, 5 dic 2025 a la(s) 6:05?a.m., Per-Ake Minborg > (per-ake.minborg at oracle.com) escribi?: > > Hi David, > > Thank you for trying out LazyConstant and providing > feedback. That is precisely what previews are for! > > If you take a closer look at the specification of > |LazyConstant::orElse,|?it says that the method will > /never trigger initialization./?And so, you /can/?actually > be sure that in your first example, |foo|?is always > initialized to "hello" (if ever initialized). It is only > if foo is not initialized that the method will return > "hello2" (again, without initializing foo). This is > similar to how |Optional|?works. > > It would be possible to entirely remove the > |orElse()|?method from the API, and in the rare cases > where an equivalent functionality is called for, rely on > |LazyConstant::isInitialized|?instead. > > Best, Per > > > Confidential- Oracle Internal > ------------------------------------------------------------------------ > *From:* amber-dev on behalf > of david Grajales > *Sent:* Friday, December 5, 2025 5:38 AM > *To:* amber-dev ; > core-libs-dev at openjdk.org > *Subject:* Feedback about LazyConstants API (JEP526) > Dear Java Dev Team, > > ?I am writing to provide feedback and two specific > observations regarding the LazyConstant API, which is > currently a preview feature in OpenJDK 26. > > ?I appreciate the API's direction and I think it's a good > improvement compared to its first iteration; however, I > see potential for improved expressiveness, particularly in > conditional scenarios. > > > *1. Proposal: Zero-Parameter `LazyConstant.of()` Overload:* > > Currently, the mandatory use of a factory method receiving > a `Supplier` (due to the lack of a public constructor) can > obscure the expressiveness of conditional or > multiple-value initialization paths. **The Issue:** When > looking at the declaration: > > LazyConstant foo = LazyConstant.of(() -> "hello"); > > the code gives the strong, immediate impression that the > value is *always* initialized to |"hello"|. This makes it > difficult to infer that the constant might ultimately > resolve to an alternative value set later via |orElse()| > or another conditional path, especially when skimming the > code: > > LazyConstant foo = LazyConstant.of(() -> "hello"); > // When skimming the code it's not always obvious that > this may not be the actual value > void main() { > if (someCondition()) { > ? ? ? ? ? foo.get(); // Trigger initialization to "hello" > ?} > // If someCondition is false, the final value of foo is > determined here: > var res1 = foo.orElse("hello2"); // ... > } > > *My Suggestion:* I propose introducing a *zero-parameter > overloaded static factory method* |of()|: > > LazyConstant foo = LazyConstant.of(); > > This form explicitly communicates that the constant is > initialized to an *unresolved* state, suggesting that the > value will be determined downstream by the first > invocation of an initialization/computation method. > > LazyConstant foo = LazyConstant.of(); // Clearly > unresolved > void main() { > if (someCondition()) { > ? ? ? foo.orElse("hello"); > ?} > var res1 = foo.orElse("hello2"); // ... > } > > This is specially useful for clarity when one has > conditional initialization in places such as the > constructor of a class. For example > > private class Bar{ > ? ? LazyConstant foo = LazyConstant.of(); > ? ? private Bar(Some some){ > ? ? ? ? if(some.condition()){ > ? ? ? ? ? ? foo.orElse("foo"); > ? ? ? ? } > ? ? ? ? foo.orElse("foo2"); > ? ? } > > ? ? String computeValue() { > ? ? ? ? return "hello"; > ? ? } > > ? ? String computeValue2(){ > ? ? ? ? return "hello2"; > ? ? } > } > > > 2. Method Naming Suggestion and and supplier in > instance method for consistency in the API > > My second, much more minor observation relates to the > instance method |orElse(T t)|. > > While |orElse| fits a retrieval pattern, I personally feel > that *|compute|* or *|computeIfAbsent|* would better > express the intent of this method, as its primary function > is not just to retrieve, but to trigger the computation > and *set the final value* of the constant if it is > currently uninitialized. Also, as the factory of() has a > supplier i think this instance method should also receive > a Supplier, This not only keeps the API consistent in the > usage but makes more ergonomic the declaration of complex > initialization logic inside the method. > > > private class Bar{ > ? ? LazyConstant foo = > LazyConstant.of(InitParam::default); // Under the current > API this is mandatory but in reality the value is set in > the constructor, default is never really used. > ? ? private Bar(Some some){ > ?foo.compute(some::executeCallToCacheDBAndBringInitializationParams) > //Real configuration happens here > > ? ? } > } > > This last it's very common for initialization of > configuration classes and singletons. > > > Thank you so much for your attention, I hope you find this > feedback useful. > > Always yours. David Grajales > -------------- next part -------------- An HTML attachment was scrubbed... URL: From kan.izh at gmail.com Tue Dec 9 11:15:03 2025 From: kan.izh at gmail.com (Anatoly Kupriyanov) Date: Tue, 9 Dec 2025 11:15:03 +0000 Subject: [External] : Re: Feedback about LazyConstants API (JEP526) In-Reply-To: <10add665-86f5-4a5e-9b4b-d4ceb20b4494@oracle.com> References: <10add665-86f5-4a5e-9b4b-d4ceb20b4494@oracle.com> Message-ID: That's seems exactly opposite. The `get` returns initial value always making the constant initialised by calling compute. The `orElse(v)` is a shortcut for `c.isInitialized() ? c.get() : v`. WBR, Anatoly. On Tue, 9 Dec 2025, 10:25 Maurizio Cimadamore, < maurizio.cimadamore at oracle.com> wrote: > I agree with most of the conclusions in this thread. > > > One small nit is that, in reality, `orElse` is a "primitive" in disguise. > E.g. you can implement `get` in terms of `orElse` but not the other way > around (unless you are willing to do _two_ accessed to the underlying > value). So, while we could drop it, we would also lose something (which is > why we decided to keep it, at least for now). > > > Maurizio > > > > On 08/12/2025 12:31, Per-Ake Minborg wrote: > > So, it is nice that folks seem to agree that LazyConstant should only > compute and initialize its contents from the Supplier/lambda given at > declaration time. The orElse method seems to blur the contours of > LazyConstant , and so, as previously said, we might consider removing the > method altogether in the next preview. > > It is also a fact that many have identified a need for "something else > more low-level" that supports a more imperative programming model when > working with constants that are lazily set. We do not rule out that such a > thing might appear in a future JDK version. > > Best, Per > > Confidential- Oracle Internal > ------------------------------ > *From:* David Alayachew > > *Sent:* Friday, December 5, 2025 2:51 PM > *To:* Red IO > *Cc:* david Grajales > ; Per-Ake Minborg > ; amber-dev > ; core-libs-dev > > *Subject:* [External] : Re: Feedback about LazyConstants API (JEP526) > > Caveat -- I have only used the Java 25 version of this library. > > I agree that the name orElse() is not intuitive. It was made more > intuitive by the existence of orElseSet(). In its absence, changing the > name makes sense. > > Though, I'm definitely open to just removing the method. This is easy > enough to accomplish ourselves. Would prefer a rename though. > > On Fri, Dec 5, 2025, 8:32?AM Red IO wrote: > > Hi David, > As par already said the orElse method doesn't initializes the > LazyConstant. > It just checks rather the value is init and if not calls the supplier to > get a substitute for the missing constant. > Example: > LazyConstant x = LazyConstant.of(() -> "Const"); > var uninit1 = x.orElse(() -> "substitute 1"); > var uninit2 = x.orElse(() -> "substitute 2"); > var init1 = x.get(); > var init2 = x.orElse(() -> "substitute 3"); > uninit1 and uninit2 get the substitute 1/2 > And init1 and init2 get Const. > > This is surprising if you expect it to be a way to init it with an > alternative value. > > My suggestion would to make the separation clear and allow for another use > case by spliting this api in 2 parts: > One class LazyConstant > Takes a Supplier in static factory and exposes get() > > And > Class LazyInit > Which takes no arguments in the static factory and takes a supplier in the > get method that gets called when get is called for the first time. > In this case the source for the constant can be any piece of code that has > access to the LazyConstant. This might be desired in some cases. In cases > where it's not the other version can be used. > > This split makes it clear from which context the constant is initialized > from (consumer or at declaration) > > Mixing those 2 or having methods that appear to do this is rather > confusing. > > > > One solution for the "i might not want to init the constant" case the > "orElse" method is meant to be is to have a method "tryGet" which returns > Optional instead. This makes it clear that the value might not be there and > is not initialized when calling the method. Nobody expects to init the > constant when calling orElse on a returned Optional. > > My 2 suggestions here are completely independent and should be viewed as > such. > > Great regards > RedIODev > > On Fri, Dec 5, 2025, 13:55 david Grajales > wrote: > > HI Per. I pleasure to talk with you. > > You are right about one thing but this actually makes the API less > intuitive and harder to read and reason about. > > LazyConstant foo = LazyConstant.of(() -> "hello"); > > void main() { > if (someCondition()) {// asume false > foo.get(); > } > foo.orElse("hello2"); // ... > > println(foo.get()); // This prints "hello" > } > > But if one assigns foo.orElse("hello2") to a variable, the variable > actually gets the "hello2" value. > > void main() { > if (someCondition()) {// asume false > foo.get(); > } > var res = foo.orElse("hello2"); // ... > var res2 = foo.orElse("hello3"); > println(res); // This prints "hello2" > println(res2);//This prints "hello3" > } > > This is actually even more confusing and makes the API more error prone. I > personally think once initialized the lazy constant should always return > the same value (maybe through the .get() method only), and there should not > be any possibility of getting a different values from the same instance > either in the .of() static method or in any hypothetical instance method > for conditional downstream logic. I guess one could achieve the latter > with the static factory method through something like this (although less > elegant) > > private class Bar{ > private final LazyConstant foo; > private Bar(Some some){ > > if(some.condition){ > foo = LazyConstant.of(() -> "hello"); > }else { > foo = LazyConstant.of(() -> "hello2"); > } > } > } > > Thank you for reading. This is all I have to report. > > Best regards. > > > > El vie, 5 dic 2025 a la(s) 6:05?a.m., Per-Ake Minborg ( > per-ake.minborg at oracle.com) escribi?: > > Hi David, > > Thank you for trying out LazyConstant and providing feedback. That is > precisely what previews are for! > > If you take a closer look at the specification of LazyConstant::orElse, it > says that the method will *never trigger initialization.* And so, you > *can* actually be sure that in your first example, foo is always > initialized to "hello" (if ever initialized). It is only if foo is not > initialized that the method will return "hello2" (again, without > initializing foo). This is similar to how Optional works. > > It would be possible to entirely remove the orElse() method from the API, > and in the rare cases where an equivalent functionality is called for, rely > on LazyConstant::isInitialized instead. > > Best, Per > > > Confidential- Oracle Internal > ------------------------------ > *From:* amber-dev on behalf of david > Grajales > *Sent:* Friday, December 5, 2025 5:38 AM > *To:* amber-dev ; core-libs-dev at openjdk.org < > core-libs-dev at openjdk.org> > *Subject:* Feedback about LazyConstants API (JEP526) > > Dear Java Dev Team, > > I am writing to provide feedback and two specific observations regarding > the LazyConstant API, which is currently a preview feature in OpenJDK 26. > > I appreciate the API's direction and I think it's a good improvement > compared to its first iteration; however, I see potential for improved > expressiveness, particularly in conditional scenarios. > > > *1. Proposal: Zero-Parameter `LazyConstant.of()` Overload:* > > Currently, the mandatory use of a factory method receiving a `Supplier` > (due to the lack of a public constructor) can obscure the expressiveness of > conditional or multiple-value initialization paths. **The Issue:** When > looking at the declaration: > > LazyConstant foo = LazyConstant.of(() -> "hello"); > > the code gives the strong, immediate impression that the value is *always* > initialized to "hello". This makes it difficult to infer that the > constant might ultimately resolve to an alternative value set later via > orElse() or another conditional path, especially when skimming the code: > > LazyConstant foo = LazyConstant.of(() -> "hello"); // When > skimming the code it's not always obvious that this may not be the actual > value > > void main() { > if (someCondition()) { > foo.get(); // Trigger initialization to "hello" > } > // If someCondition is false, the final value of foo is determined here: > > var res1 = foo.orElse("hello2"); // ... > } > > *My Suggestion:* I propose introducing a *zero-parameter overloaded > static factory method* of(): > > LazyConstant foo = LazyConstant.of(); > > This form explicitly communicates that the constant is initialized to an * > unresolved* state, suggesting that the value will be determined > downstream by the first invocation of an initialization/computation method. > > LazyConstant foo = LazyConstant.of(); // Clearly unresolved > void main() { > if (someCondition()) { > foo.orElse("hello"); > } > var res1 = foo.orElse("hello2"); // ... > } > > This is specially useful for clarity when one has conditional > initialization in places such as the constructor of a class. For example > > private class Bar{ > LazyConstant foo = LazyConstant.of(); > private Bar(Some some){ > if(some.condition()){ > foo.orElse("foo"); > } > foo.orElse("foo2"); > } > > String computeValue() { > return "hello"; > } > > String computeValue2(){ > return "hello2"; > } > } > 2. Method Naming Suggestion and and supplier in instance method for > consistency in the API > > My second, much more minor observation relates to the instance method orElse(T > t). > > While orElse fits a retrieval pattern, I personally feel that * compute* > or *computeIfAbsent* would better express the intent of this method, as > its primary function is not just to retrieve, but to trigger the > computation and *set the final value* of the constant if it is currently > uninitialized. Also, as the factory of() has a supplier i think this > instance method should also receive a Supplier, This not only keeps the API > consistent in the usage but makes more ergonomic the declaration of complex > initialization logic inside the method. > > > private class Bar{ > LazyConstant foo = LazyConstant.of(InitParam::default); // > Under the current API this is mandatory but in reality the value is set in > the constructor, default is never really used. > private Bar(Some some){ > foo.compute(some::executeCallToCacheDBAndBringInitializationParams) > //Real configuration happens here > > } > } > > This last it's very common for initialization of configuration classes and > singletons. > > > Thank you so much for your attention, I hope you find this feedback useful. > > Always yours. David Grajales > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Tue Dec 9 11:22:13 2025 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Tue, 9 Dec 2025 11:22:13 +0000 Subject: [External] : Re: Feedback about LazyConstants API (JEP526) In-Reply-To: References: <10add665-86f5-4a5e-9b4b-d4ceb20b4494@oracle.com> Message-ID: <37152b24-6f4c-4fb9-a2f8-c444d3be58e6@oracle.com> On 09/12/2025 11:15, Anatoly Kupriyanov wrote: > That's seems exactly opposite. The `get` returns initial value always > making the constant initialised by calling compute. > The `orElse(v)` is a shortcut for `c.isInitialized() ? c.get() : v`. Not really :-) c.isInitialized also calls `getAcquire` internally (so your snippet would end up calling getAcquire twice in some? paths, once for isInitialized, and another for get). Technically, get() can be explained as: V value = lazyConstant.orElse(sentinel) if (value == sentinel) throw new NoSuchElementException That performs only one call to getAcquire, not two. (We don't implement it exactly this way, but we could). Maurizio > > WBR, Anatoly. > > On Tue, 9 Dec 2025, 10:25 Maurizio Cimadamore, > wrote: > > I agree with most of the conclusions in this thread. > > > One small nit is that, in reality, `orElse` is a "primitive" in > disguise. E.g. you can implement `get` in terms of `orElse` but > not the other way around (unless you are willing to do _two_ > accessed to the underlying value). So, while we could drop it, we > would also lose something (which is why we decided to keep it, at > least for now). > > > Maurizio > > > > On 08/12/2025 12:31, Per-Ake Minborg wrote: >> So, it is nice that folks seem to agree that >> |LazyConstant|?should only compute and initialize its contents >> from the Supplier/lambda given at declaration time. The >> |orElse|?method seems to blur the contours of |LazyConstant|?, >> and so, as previously said, we might consider removing the method >> altogether in the next preview. >> >> It is also a fact that many have identified a need for "something >> else more low-level" that supports a more imperative programming >> model when working with constants that are lazily set. We do not >> rule out that such a thing might appear in a future JDK version. >> >> Best, Per >> >> Confidential- Oracle Internal >> ------------------------------------------------------------------------ >> *From:* David Alayachew >> >> *Sent:* Friday, December 5, 2025 2:51 PM >> *To:* Red IO >> >> *Cc:* david Grajales >> ; Per-Ake Minborg >> ; >> amber-dev ; >> core-libs-dev >> >> *Subject:* [External] : Re: Feedback about LazyConstants API >> (JEP526) >> Caveat -- I have only used the Java 25 version of this library. >> >> I agree that the name orElse() is not intuitive. It was made more >> intuitive by the existence of orElseSet(). In its absence, >> changing the name makes sense. >> >> Though, I'm definitely open to just removing the method. This is >> easy enough to accomplish ourselves. Would prefer a rename though. >> >> On Fri, Dec 5, 2025, 8:32?AM Red IO >> wrote: >> >> Hi David, >> As par already said the orElse method doesn't initializes the >> LazyConstant. >> It just checks rather the value is init and if not calls the >> supplier to get a substitute for the missing constant. >> Example: >> LazyConstant x = LazyConstant.of(() -> "Const"); >> var uninit1 = x.orElse(() -> "substitute 1"); >> var uninit2 = x.orElse(() -> "substitute 2"); >> var init1 = x.get(); >> var init2 = x.orElse(() -> "substitute 3"); >> uninit1 and uninit2 get the substitute 1/2 >> And init1 and init2 get Const. >> >> This is surprising if you expect it to be a way to init it >> with an alternative value. >> >> My suggestion would to make the separation clear and allow >> for another use case by spliting this api in 2 parts: >> One class LazyConstant >> Takes a Supplier in static factory and exposes get() >> >> And >> Class LazyInit >> Which takes no arguments in the static factory and takes a >> supplier in the get method that gets called when get is >> called for the first time. >> In this case the source for the constant can be any piece of >> code that has access to the LazyConstant. This might be >> desired in some cases. In cases where it's not the other >> version can be used. >> >> This split makes it clear from which context the constant is >> initialized from (consumer or at declaration) >> >> Mixing those 2 or having methods that appear to do this is >> rather confusing. >> >> >> >> One solution for the "i might not want to init the constant" >> case the "orElse" method is meant to be is to have a method >> "tryGet" which returns Optional instead. This makes it clear >> that the value might not be there and is not initialized when >> calling the method. Nobody expects to init the constant when >> calling orElse on a returned Optional. >> >> My 2 suggestions here are completely independent and should >> be viewed as such. >> >> Great regards >> RedIODev >> >> On Fri, Dec 5, 2025, 13:55 david Grajales >> wrote: >> >> HI Per. I pleasure to talk with you. >> >> You are right about one thing but this actually makes the >> API less intuitive and harder to read and reason about. >> >> LazyConstant foo = LazyConstant.of(() -> "hello"); >> >> void main() { >> ? ? if (someCondition()) {// asume false >> ? ? ? ? foo.get(); >> ? ? } >> ? ? foo.orElse("hello2"); // ... >> >> ? ? println(foo.get()); // This prints "hello" >> } >> >> But if one assigns foo.orElse("hello2") to a variable, >> the variable actually gets the "hello2" value. >> >> void main() { >> ? ? if (someCondition()) {// asume false >> ? ? ? ? foo.get(); >> ? ? } >> ? ? var res = foo.orElse("hello2"); // ... >> ? ? var res2 = foo.orElse("hello3"); >> ? ? println(res); // This prints "hello2" >> ? ? println(res2);//This prints "hello3" >> } >> >> This is actually even more confusing and makes the API >> more error prone. I personally think once initialized the >> lazy constant should always return the same value (maybe >> through the .get() method only), and there should not be >> any possibility of getting a different values?from the >> same instance either in the .of() static method or in any >> hypothetical instance method for conditional downstream >> logic.? I guess one could achieve the latter with the >> static factory method through?something like this >> (although?less elegant) >> >> private class Bar{ >> ? ? private final LazyConstant foo; >> ? ? private Bar(Some some){ >> >> ? ? ? ? if(some.condition){ >> ? ? ? ? ? ? foo = LazyConstant.of(() -> "hello"); >> ? ? ? ? }else { >> ? ? ? ? ? ? foo = LazyConstant.of(() -> "hello2"); >> ? ? ? ? } >> ? ? } >> } >> >> Thank you for reading. This is all I have to report. >> >> Best regards. >> >> >> >> El vie, 5 dic 2025 a la(s) 6:05?a.m., Per-Ake Minborg >> (per-ake.minborg at oracle.com) escribi?: >> >> Hi David, >> >> Thank you for trying out LazyConstant and providing >> feedback. That is precisely what previews are for! >> >> If you take a closer look at the specification of >> |LazyConstant::orElse,|?it says that the method will >> /never trigger initialization./?And so, you >> /can/?actually be sure that in your first example, >> |foo|?is always initialized to "hello" (if ever >> initialized). It is only if foo is not initialized >> that the method will return "hello2" (again, without >> initializing foo). This is similar to how >> |Optional|?works. >> >> It would be possible to entirely remove the >> |orElse()|?method from the API, and in the rare cases >> where an equivalent functionality is called for, rely >> on |LazyConstant::isInitialized|?instead. >> >> Best, Per >> >> >> Confidential- Oracle Internal >> ------------------------------------------------------------------------ >> *From:* amber-dev on >> behalf of david Grajales >> *Sent:* Friday, December 5, 2025 5:38 AM >> *To:* amber-dev ; >> core-libs-dev at openjdk.org >> *Subject:* Feedback about LazyConstants API (JEP526) >> Dear Java Dev Team, >> >> ?I am writing to provide feedback and two specific >> observations regarding the LazyConstant API, which is >> currently a preview feature in OpenJDK 26. >> >> ?I appreciate the API's direction and I think it's a >> good improvement compared to its first iteration; >> however, I see potential for improved expressiveness, >> particularly in conditional scenarios. >> >> >> *1. Proposal: Zero-Parameter `LazyConstant.of()` >> Overload:* >> >> Currently, the mandatory use of a factory method >> receiving a `Supplier` (due to the lack of a public >> constructor) can obscure the expressiveness of >> conditional or multiple-value initialization paths. >> **The Issue:** When looking at the declaration: >> >> LazyConstant foo = LazyConstant.of(() -> >> "hello"); >> >> the code gives the strong, immediate impression that >> the value is *always* initialized to |"hello"|. This >> makes it difficult to infer that the constant might >> ultimately resolve to an alternative value set later >> via |orElse()| or another conditional path, >> especially when skimming the code: >> >> LazyConstant foo = LazyConstant.of(() -> >> "hello"); // When skimming the code it's not always >> obvious that this may not be the actual value >> void main() { >> if (someCondition()) { >> ? ? ? ? ? foo.get(); // Trigger initialization to >> "hello" >> ?} >> // If someCondition is false, the final value of foo >> is determined here: >> var res1 = foo.orElse("hello2"); // ... >> } >> >> *My Suggestion:* I propose introducing a >> *zero-parameter overloaded static factory method* |of()|: >> >> LazyConstant foo = LazyConstant.of(); >> >> This form explicitly communicates that the constant >> is initialized to an *unresolved* state, suggesting >> that the value will be determined downstream by the >> first invocation of an initialization/computation method. >> >> LazyConstant foo = LazyConstant.of(); // >> Clearly unresolved >> void main() { >> if (someCondition()) { >> ? ? ? foo.orElse("hello"); >> ?} >> var res1 = foo.orElse("hello2"); // ... >> } >> >> This is specially useful for clarity when one has >> conditional initialization in places such as the >> constructor of a class. For example >> >> private class Bar{ >> ? ? LazyConstant foo = LazyConstant.of(); >> ? ? private Bar(Some some){ >> ? ? ? ? if(some.condition()){ >> ? ? ? ? ? ? foo.orElse("foo"); >> ? ? ? ? } >> ? ? ? ? foo.orElse("foo2"); >> ? ? } >> >> ? ? String computeValue() { >> ? ? ? ? return "hello"; >> ? ? } >> >> ? ? String computeValue2(){ >> ? ? ? ? return "hello2"; >> ? ? } >> } >> >> >> 2. Method Naming Suggestion and and supplier in >> instance method for consistency in the API >> >> My second, much more minor observation relates to the >> instance method |orElse(T t)|. >> >> While |orElse| fits a retrieval pattern, I personally >> feel that *|compute|* or *|computeIfAbsent|* would >> better express the intent of this method, as its >> primary function is not just to retrieve, but to >> trigger the computation and *set the final value* of >> the constant if it is currently uninitialized. Also, >> as the factory of() has a supplier i think this >> instance method should also receive a Supplier, This >> not only keeps the API consistent in the usage but >> makes more ergonomic the declaration of complex >> initialization logic inside the method. >> >> >> private class Bar{ >> LazyConstant foo = >> LazyConstant.of(InitParam::default); // Under the >> current API this is mandatory but in reality the >> value is set in the constructor, default is never >> really used. >> ? ? private Bar(Some some){ >> ?foo.compute(some::executeCallToCacheDBAndBringInitializationParams) >> //Real configuration happens here >> >> ? ? } >> } >> >> This last it's very common for initialization of >> configuration classes and singletons. >> >> >> Thank you so much for your attention, I hope you find >> this feedback useful. >> >> Always yours. David Grajales >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Tue Dec 9 11:25:07 2025 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Tue, 9 Dec 2025 11:25:07 +0000 Subject: [External] : Re: Feedback about LazyConstants API (JEP526) In-Reply-To: <37152b24-6f4c-4fb9-a2f8-c444d3be58e6@oracle.com> References: <10add665-86f5-4a5e-9b4b-d4ceb20b4494@oracle.com> <37152b24-6f4c-4fb9-a2f8-c444d3be58e6@oracle.com> Message-ID: <7f79f147-c9f1-4411-94dc-81b737def634@oracle.com> Hit send too fast... Yes, orElse alone can't set/compute the uninitialized value. So that's a problem (as you observed) But orElse can't also be explained in terms of get() -- unless you accept doing a double access of the underlying storage. Uplevelling, I believe the fact that, in the current form, neither API sits cleanly on top of the other is probably a part of the problem highlighted in this discussion. Maurizio On 09/12/2025 11:22, Maurizio Cimadamore wrote: > > > On 09/12/2025 11:15, Anatoly Kupriyanov wrote: >> That's seems exactly opposite. The `get` returns initial value always >> making the constant initialised by calling compute. >> The `orElse(v)` is a shortcut for `c.isInitialized() ? c.get() : v`. > > Not really :-) > > c.isInitialized also calls `getAcquire` internally (so your snippet > would end up calling getAcquire twice in some? paths, once for > isInitialized, and another for get). > > Technically, get() can be explained as: > > V value = lazyConstant.orElse(sentinel) > if (value == sentinel) throw new NoSuchElementException > > That performs only one call to getAcquire, not two. > > (We don't implement it exactly this way, but we could). > > Maurizio > >> >> WBR, Anatoly. >> >> On Tue, 9 Dec 2025, 10:25 Maurizio Cimadamore, >> wrote: >> >> I agree with most of the conclusions in this thread. >> >> >> One small nit is that, in reality, `orElse` is a "primitive" in >> disguise. E.g. you can implement `get` in terms of `orElse` but >> not the other way around (unless you are willing to do _two_ >> accessed to the underlying value). So, while we could drop it, we >> would also lose something (which is why we decided to keep it, at >> least for now). >> >> >> Maurizio >> >> >> >> On 08/12/2025 12:31, Per-Ake Minborg wrote: >>> So, it is nice that folks seem to agree that >>> |LazyConstant|?should only compute and initialize its contents >>> from the Supplier/lambda given at declaration time. The >>> |orElse|?method seems to blur the contours of |LazyConstant|?, >>> and so, as previously said, we might consider removing the >>> method altogether in the next preview. >>> >>> It is also a fact that many have identified a need for >>> "something else more low-level" that supports a more imperative >>> programming model when working with constants that are lazily >>> set. We do not rule out that such a thing might appear in a >>> future JDK version. >>> >>> Best, Per >>> >>> Confidential- Oracle Internal >>> ------------------------------------------------------------------------ >>> *From:* David Alayachew >>> >>> *Sent:* Friday, December 5, 2025 2:51 PM >>> *To:* Red IO >>> >>> *Cc:* david Grajales >>> ; Per-Ake Minborg >>> >>> ; amber-dev >>> ; >>> core-libs-dev >>> >>> *Subject:* [External] : Re: Feedback about LazyConstants API >>> (JEP526) >>> Caveat -- I have only used the Java 25 version of this library. >>> >>> I agree that the name orElse() is not intuitive. It was made >>> more intuitive by the existence of orElseSet(). In its absence, >>> changing the name makes sense. >>> >>> Though, I'm definitely open to just removing the method. This is >>> easy enough to accomplish ourselves. Would prefer a rename though. >>> >>> On Fri, Dec 5, 2025, 8:32?AM Red IO >>> wrote: >>> >>> Hi David, >>> As par already said the orElse method doesn't initializes >>> the LazyConstant. >>> It just checks rather the value is init and if not calls the >>> supplier to get a substitute for the missing constant. >>> Example: >>> LazyConstant x = LazyConstant.of(() -> "Const"); >>> var uninit1 = x.orElse(() -> "substitute 1"); >>> var uninit2 = x.orElse(() -> "substitute 2"); >>> var init1 = x.get(); >>> var init2 = x.orElse(() -> "substitute 3"); >>> uninit1 and uninit2 get the substitute 1/2 >>> And init1 and init2 get Const. >>> >>> This is surprising if you expect it to be a way to init it >>> with an alternative value. >>> >>> My suggestion would to make the separation clear and allow >>> for another use case by spliting this api in 2 parts: >>> One class LazyConstant >>> Takes a Supplier in static factory and exposes get() >>> >>> And >>> Class LazyInit >>> Which takes no arguments in the static factory and takes a >>> supplier in the get method that gets called when get is >>> called for the first time. >>> In this case the source for the constant can be any piece of >>> code that has access to the LazyConstant. This might be >>> desired in some cases. In cases where it's not the other >>> version can be used. >>> >>> This split makes it clear from which context the constant is >>> initialized from (consumer or at declaration) >>> >>> Mixing those 2 or having methods that appear to do this is >>> rather confusing. >>> >>> >>> >>> One solution for the "i might not want to init the constant" >>> case the "orElse" method is meant to be is to have a method >>> "tryGet" which returns Optional instead. This makes it clear >>> that the value might not be there and is not initialized >>> when calling the method. Nobody expects to init the constant >>> when calling orElse on a returned Optional. >>> >>> My 2 suggestions here are completely independent and should >>> be viewed as such. >>> >>> Great regards >>> RedIODev >>> >>> On Fri, Dec 5, 2025, 13:55 david Grajales >>> wrote: >>> >>> HI Per. I pleasure to talk with you. >>> >>> You are right about one thing but this actually makes >>> the API less intuitive and harder to read and reason about. >>> >>> LazyConstant foo = LazyConstant.of(() -> "hello"); >>> >>> void main() { >>> ? ? if (someCondition()) {// asume false >>> ? ? ? ? foo.get(); >>> ? ? } >>> ? ? foo.orElse("hello2"); // ... >>> >>> ? ? println(foo.get()); // This prints "hello" >>> } >>> >>> But if one assigns foo.orElse("hello2") to a variable, >>> the variable actually gets the "hello2" value. >>> >>> void main() { >>> ? ? if (someCondition()) {// asume false >>> ? ? ? ? foo.get(); >>> ? ? } >>> ? ? var res = foo.orElse("hello2"); // ... >>> ? ? var res2 = foo.orElse("hello3"); >>> ? ? println(res); // This prints "hello2" >>> ? ? println(res2);//This prints "hello3" >>> } >>> >>> This is actually even more confusing and makes the API >>> more error prone. I personally think once initialized >>> the lazy constant should always return the same value >>> (maybe through the .get() method only), and there should >>> not be any possibility of getting a different >>> values?from the same instance either in the .of() static >>> method or in any hypothetical instance method for >>> conditional downstream logic.? I guess one could achieve >>> the latter with the static factory method >>> through?something like this (although?less elegant) >>> >>> private class Bar{ >>> ? ? private final LazyConstant foo; >>> ? ? private Bar(Some some){ >>> >>> ? ? ? ? if(some.condition){ >>> ? ? ? ? ? ? foo = LazyConstant.of(() -> "hello"); >>> ? ? ? ? }else { >>> ? ? ? ? ? ? foo = LazyConstant.of(() -> "hello2"); >>> ? ? ? ? } >>> ? ? } >>> } >>> >>> Thank you for reading. This is all I have to report. >>> >>> Best regards. >>> >>> >>> >>> El vie, 5 dic 2025 a la(s) 6:05?a.m., Per-Ake Minborg >>> (per-ake.minborg at oracle.com) escribi?: >>> >>> Hi David, >>> >>> Thank you for trying out LazyConstant and providing >>> feedback. That is precisely what previews are for! >>> >>> If you take a closer look at the specification of >>> |LazyConstant::orElse,|?it says that the method will >>> /never trigger initialization./?And so, you >>> /can/?actually be sure that in your first example, >>> |foo|?is always initialized to "hello" (if ever >>> initialized). It is only if foo is not initialized >>> that the method will return "hello2" (again, without >>> initializing foo). This is similar to how >>> |Optional|?works. >>> >>> It would be possible to entirely remove the >>> |orElse()|?method from the API, and in the rare >>> cases where an equivalent functionality is called >>> for, rely on |LazyConstant::isInitialized|?instead. >>> >>> Best, Per >>> >>> >>> Confidential- Oracle Internal >>> ------------------------------------------------------------------------ >>> *From:* amber-dev on >>> behalf of david Grajales >>> *Sent:* Friday, December 5, 2025 5:38 AM >>> *To:* amber-dev ; >>> core-libs-dev at openjdk.org >>> *Subject:* Feedback about LazyConstants API (JEP526) >>> Dear Java Dev Team, >>> >>> ?I am writing to provide feedback and two specific >>> observations regarding the LazyConstant API, which >>> is currently a preview feature in OpenJDK 26. >>> >>> ?I appreciate the API's direction and I think it's a >>> good improvement compared to its first iteration; >>> however, I see potential for improved >>> expressiveness, particularly in conditional scenarios. >>> >>> >>> *1. Proposal: Zero-Parameter `LazyConstant.of()` >>> Overload:* >>> >>> Currently, the mandatory use of a factory method >>> receiving a `Supplier` (due to the lack of a public >>> constructor) can obscure the expressiveness of >>> conditional or multiple-value initialization paths. >>> **The Issue:** When looking at the declaration: >>> >>> LazyConstant foo = LazyConstant.of(() -> >>> "hello"); >>> >>> the code gives the strong, immediate impression that >>> the value is *always* initialized to |"hello"|. This >>> makes it difficult to infer that the constant might >>> ultimately resolve to an alternative value set later >>> via |orElse()| or another conditional path, >>> especially when skimming the code: >>> >>> LazyConstant foo = LazyConstant.of(() -> >>> "hello"); // When skimming the code it's not always >>> obvious that this may not be the actual value >>> void main() { >>> if (someCondition()) { >>> ? ? ? ? ? foo.get(); // Trigger initialization to >>> "hello" >>> ?} >>> // If someCondition is false, the final value of foo >>> is determined here: >>> var res1 = foo.orElse("hello2"); // ... >>> } >>> >>> *My Suggestion:* I propose introducing a >>> *zero-parameter overloaded static factory method* >>> |of()|: >>> >>> LazyConstant foo = LazyConstant.of(); >>> >>> This form explicitly communicates that the constant >>> is initialized to an *unresolved* state, suggesting >>> that the value will be determined downstream by the >>> first invocation of an initialization/computation >>> method. >>> >>> LazyConstant foo = LazyConstant.of(); // >>> Clearly unresolved >>> void main() { >>> if (someCondition()) { >>> ? ? ? foo.orElse("hello"); >>> ?} >>> var res1 = foo.orElse("hello2"); // ... >>> } >>> >>> This is specially useful for clarity when one has >>> conditional initialization in places such as the >>> constructor of a class. For example >>> >>> private class Bar{ >>> ? ? LazyConstant foo = LazyConstant.of(); >>> ? ? private Bar(Some some){ >>> ? ? ? ? if(some.condition()){ >>> ? ? ? ? ? ? foo.orElse("foo"); >>> ? ? ? ? } >>> ? ? ? ? foo.orElse("foo2"); >>> ? ? } >>> >>> ? ? String computeValue() { >>> ? ? ? ? return "hello"; >>> ? ? } >>> >>> ? ? String computeValue2(){ >>> ? ? ? ? return "hello2"; >>> ? ? } >>> } >>> >>> >>> 2. Method Naming Suggestion and and supplier >>> in instance method for consistency in the API >>> >>> My second, much more minor observation relates to >>> the instance method |orElse(T t)|. >>> >>> While |orElse| fits a retrieval pattern, I >>> personally feel that *|compute|* or >>> *|computeIfAbsent|* would better express the intent >>> of this method, as its primary function is not just >>> to retrieve, but to trigger the computation and *set >>> the final value* of the constant if it is currently >>> uninitialized. Also, as the factory of() has a >>> supplier i think this instance method should also >>> receive a Supplier, This not only keeps the API >>> consistent in the usage but makes more ergonomic the >>> declaration of complex initialization logic inside >>> the method. >>> >>> >>> private class Bar{ >>> LazyConstant foo = >>> LazyConstant.of(InitParam::default); // Under the >>> current API this is mandatory but in reality the >>> value is set in the constructor, default is never >>> really used. >>> ? ? private Bar(Some some){ >>> ?foo.compute(some::executeCallToCacheDBAndBringInitializationParams) >>> //Real configuration happens here >>> >>> ? ? } >>> } >>> >>> This last it's very common for initialization of >>> configuration classes and singletons. >>> >>> >>> Thank you so much for your attention, I hope you >>> find this feedback useful. >>> >>> Always yours. David Grajales >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From redio.development at gmail.com Tue Dec 9 11:37:23 2025 From: redio.development at gmail.com (Red IO) Date: Tue, 9 Dec 2025 12:37:23 +0100 Subject: [External] : Re: Feedback about LazyConstants API (JEP526) In-Reply-To: <10add665-86f5-4a5e-9b4b-d4ceb20b4494@oracle.com> References: <10add665-86f5-4a5e-9b4b-d4ceb20b4494@oracle.com> Message-ID: You can implement orElse in combination with a test function that returns a boolean rather the value is there already. I don't see orElse as such a great primitive. At this point I want to advertise my tryGet suggestion that returns an Option again as I think it would satisfy the people wanting orElse without the confusion orElse created in this discussion alone. Also in this sense tryGet is the more pure form of the "primitive" as it returns basically a tuple of the value and a boolean rather the value is there. Which is pretty much the entire state the LazyConstant is carrying without any transformation of information. Great regards RedIODev On Tue, Dec 9, 2025, 11:25 Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > I agree with most of the conclusions in this thread. > > > One small nit is that, in reality, `orElse` is a "primitive" in disguise. > E.g. you can implement `get` in terms of `orElse` but not the other way > around (unless you are willing to do _two_ accessed to the underlying > value). So, while we could drop it, we would also lose something (which is > why we decided to keep it, at least for now). > > > Maurizio > > > > On 08/12/2025 12:31, Per-Ake Minborg wrote: > > So, it is nice that folks seem to agree that LazyConstant should only > compute and initialize its contents from the Supplier/lambda given at > declaration time. The orElse method seems to blur the contours of > LazyConstant , and so, as previously said, we might consider removing the > method altogether in the next preview. > > It is also a fact that many have identified a need for "something else > more low-level" that supports a more imperative programming model when > working with constants that are lazily set. We do not rule out that such a > thing might appear in a future JDK version. > > Best, Per > > Confidential- Oracle Internal > ------------------------------ > *From:* David Alayachew > > *Sent:* Friday, December 5, 2025 2:51 PM > *To:* Red IO > *Cc:* david Grajales > ; Per-Ake Minborg > ; amber-dev > ; core-libs-dev > > *Subject:* [External] : Re: Feedback about LazyConstants API (JEP526) > > Caveat -- I have only used the Java 25 version of this library. > > I agree that the name orElse() is not intuitive. It was made more > intuitive by the existence of orElseSet(). In its absence, changing the > name makes sense. > > Though, I'm definitely open to just removing the method. This is easy > enough to accomplish ourselves. Would prefer a rename though. > > On Fri, Dec 5, 2025, 8:32?AM Red IO wrote: > > Hi David, > As par already said the orElse method doesn't initializes the > LazyConstant. > It just checks rather the value is init and if not calls the supplier to > get a substitute for the missing constant. > Example: > LazyConstant x = LazyConstant.of(() -> "Const"); > var uninit1 = x.orElse(() -> "substitute 1"); > var uninit2 = x.orElse(() -> "substitute 2"); > var init1 = x.get(); > var init2 = x.orElse(() -> "substitute 3"); > uninit1 and uninit2 get the substitute 1/2 > And init1 and init2 get Const. > > This is surprising if you expect it to be a way to init it with an > alternative value. > > My suggestion would to make the separation clear and allow for another use > case by spliting this api in 2 parts: > One class LazyConstant > Takes a Supplier in static factory and exposes get() > > And > Class LazyInit > Which takes no arguments in the static factory and takes a supplier in the > get method that gets called when get is called for the first time. > In this case the source for the constant can be any piece of code that has > access to the LazyConstant. This might be desired in some cases. In cases > where it's not the other version can be used. > > This split makes it clear from which context the constant is initialized > from (consumer or at declaration) > > Mixing those 2 or having methods that appear to do this is rather > confusing. > > > > One solution for the "i might not want to init the constant" case the > "orElse" method is meant to be is to have a method "tryGet" which returns > Optional instead. This makes it clear that the value might not be there and > is not initialized when calling the method. Nobody expects to init the > constant when calling orElse on a returned Optional. > > My 2 suggestions here are completely independent and should be viewed as > such. > > Great regards > RedIODev > > On Fri, Dec 5, 2025, 13:55 david Grajales > wrote: > > HI Per. I pleasure to talk with you. > > You are right about one thing but this actually makes the API less > intuitive and harder to read and reason about. > > LazyConstant foo = LazyConstant.of(() -> "hello"); > > void main() { > if (someCondition()) {// asume false > foo.get(); > } > foo.orElse("hello2"); // ... > > println(foo.get()); // This prints "hello" > } > > But if one assigns foo.orElse("hello2") to a variable, the variable > actually gets the "hello2" value. > > void main() { > if (someCondition()) {// asume false > foo.get(); > } > var res = foo.orElse("hello2"); // ... > var res2 = foo.orElse("hello3"); > println(res); // This prints "hello2" > println(res2);//This prints "hello3" > } > > This is actually even more confusing and makes the API more error prone. I > personally think once initialized the lazy constant should always return > the same value (maybe through the .get() method only), and there should not > be any possibility of getting a different values from the same instance > either in the .of() static method or in any hypothetical instance method > for conditional downstream logic. I guess one could achieve the latter > with the static factory method through something like this (although less > elegant) > > private class Bar{ > private final LazyConstant foo; > private Bar(Some some){ > > if(some.condition){ > foo = LazyConstant.of(() -> "hello"); > }else { > foo = LazyConstant.of(() -> "hello2"); > } > } > } > > Thank you for reading. This is all I have to report. > > Best regards. > > > > El vie, 5 dic 2025 a la(s) 6:05?a.m., Per-Ake Minborg ( > per-ake.minborg at oracle.com) escribi?: > > Hi David, > > Thank you for trying out LazyConstant and providing feedback. That is > precisely what previews are for! > > If you take a closer look at the specification of LazyConstant::orElse, it > says that the method will *never trigger initialization.* And so, you > *can* actually be sure that in your first example, foo is always > initialized to "hello" (if ever initialized). It is only if foo is not > initialized that the method will return "hello2" (again, without > initializing foo). This is similar to how Optional works. > > It would be possible to entirely remove the orElse() method from the API, > and in the rare cases where an equivalent functionality is called for, rely > on LazyConstant::isInitialized instead. > > Best, Per > > > Confidential- Oracle Internal > ------------------------------ > *From:* amber-dev on behalf of david > Grajales > *Sent:* Friday, December 5, 2025 5:38 AM > *To:* amber-dev ; core-libs-dev at openjdk.org < > core-libs-dev at openjdk.org> > *Subject:* Feedback about LazyConstants API (JEP526) > > Dear Java Dev Team, > > I am writing to provide feedback and two specific observations regarding > the LazyConstant API, which is currently a preview feature in OpenJDK 26. > > I appreciate the API's direction and I think it's a good improvement > compared to its first iteration; however, I see potential for improved > expressiveness, particularly in conditional scenarios. > > > *1. Proposal: Zero-Parameter `LazyConstant.of()` Overload:* > > Currently, the mandatory use of a factory method receiving a `Supplier` > (due to the lack of a public constructor) can obscure the expressiveness of > conditional or multiple-value initialization paths. **The Issue:** When > looking at the declaration: > > LazyConstant foo = LazyConstant.of(() -> "hello"); > > the code gives the strong, immediate impression that the value is *always* > initialized to "hello". This makes it difficult to infer that the > constant might ultimately resolve to an alternative value set later via > orElse() or another conditional path, especially when skimming the code: > > LazyConstant foo = LazyConstant.of(() -> "hello"); // When > skimming the code it's not always obvious that this may not be the actual > value > > void main() { > if (someCondition()) { > foo.get(); // Trigger initialization to "hello" > } > // If someCondition is false, the final value of foo is determined here: > > var res1 = foo.orElse("hello2"); // ... > } > > *My Suggestion:* I propose introducing a *zero-parameter overloaded > static factory method* of(): > > LazyConstant foo = LazyConstant.of(); > > This form explicitly communicates that the constant is initialized to an * > unresolved* state, suggesting that the value will be determined > downstream by the first invocation of an initialization/computation method. > > LazyConstant foo = LazyConstant.of(); // Clearly unresolved > void main() { > if (someCondition()) { > foo.orElse("hello"); > } > var res1 = foo.orElse("hello2"); // ... > } > > This is specially useful for clarity when one has conditional > initialization in places such as the constructor of a class. For example > > private class Bar{ > LazyConstant foo = LazyConstant.of(); > private Bar(Some some){ > if(some.condition()){ > foo.orElse("foo"); > } > foo.orElse("foo2"); > } > > String computeValue() { > return "hello"; > } > > String computeValue2(){ > return "hello2"; > } > } > 2. Method Naming Suggestion and and supplier in instance method for > consistency in the API > > My second, much more minor observation relates to the instance method orElse(T > t). > > While orElse fits a retrieval pattern, I personally feel that * compute* > or *computeIfAbsent* would better express the intent of this method, as > its primary function is not just to retrieve, but to trigger the > computation and *set the final value* of the constant if it is currently > uninitialized. Also, as the factory of() has a supplier i think this > instance method should also receive a Supplier, This not only keeps the API > consistent in the usage but makes more ergonomic the declaration of complex > initialization logic inside the method. > > > private class Bar{ > LazyConstant foo = LazyConstant.of(InitParam::default); // > Under the current API this is mandatory but in reality the value is set in > the constructor, default is never really used. > private Bar(Some some){ > foo.compute(some::executeCallToCacheDBAndBringInitializationParams) > //Real configuration happens here > > } > } > > This last it's very common for initialization of configuration classes and > singletons. > > > Thank you so much for your attention, I hope you find this feedback useful. > > Always yours. David Grajales > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Tue Dec 9 11:50:48 2025 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Tue, 9 Dec 2025 11:50:48 +0000 Subject: [External] : Re: Feedback about LazyConstants API (JEP526) In-Reply-To: References: <10add665-86f5-4a5e-9b4b-d4ceb20b4494@oracle.com> Message-ID: An optional-returning tryGet is one of the possible ideas floating around, yes. Maurizio On 09/12/2025 11:37, Red IO wrote: > You can implement orElse in combination with a test function that > returns a boolean rather the value is there already. I don't see > orElse as such a great primitive. > At this point I want to advertise my tryGet suggestion that returns an > Option again as I think it would satisfy the people wanting orElse > without the confusion orElse created in this discussion alone. > Also in this sense tryGet is the more pure form of the "primitive" as > it returns basically a tuple of the value and a boolean rather the > value is there. Which is pretty much the entire state the LazyConstant > is carrying without any transformation of information. > > Great regards > RedIODev > > > On Tue, Dec 9, 2025, 11:25 Maurizio Cimadamore > wrote: > > I agree with most of the conclusions in this thread. > > > One small nit is that, in reality, `orElse` is a "primitive" in > disguise. E.g. you can implement `get` in terms of `orElse` but > not the other way around (unless you are willing to do _two_ > accessed to the underlying value). So, while we could drop it, we > would also lose something (which is why we decided to keep it, at > least for now). > > > Maurizio > > > > On 08/12/2025 12:31, Per-Ake Minborg wrote: >> So, it is nice that folks seem to agree that >> |LazyConstant|?should only compute and initialize its contents >> from the Supplier/lambda given at declaration time. The >> |orElse|?method seems to blur the contours of |LazyConstant|?, >> and so, as previously said, we might consider removing the method >> altogether in the next preview. >> >> It is also a fact that many have identified a need for "something >> else more low-level" that supports a more imperative programming >> model when working with constants that are lazily set. We do not >> rule out that such a thing might appear in a future JDK version. >> >> Best, Per >> >> Confidential- Oracle Internal >> ------------------------------------------------------------------------ >> *From:* David Alayachew >> >> *Sent:* Friday, December 5, 2025 2:51 PM >> *To:* Red IO >> >> *Cc:* david Grajales >> ; Per-Ake Minborg >> ; >> amber-dev ; >> core-libs-dev >> >> *Subject:* [External] : Re: Feedback about LazyConstants API >> (JEP526) >> Caveat -- I have only used the Java 25 version of this library. >> >> I agree that the name orElse() is not intuitive. It was made more >> intuitive by the existence of orElseSet(). In its absence, >> changing the name makes sense. >> >> Though, I'm definitely open to just removing the method. This is >> easy enough to accomplish ourselves. Would prefer a rename though. >> >> On Fri, Dec 5, 2025, 8:32?AM Red IO >> wrote: >> >> Hi David, >> As par already said the orElse method doesn't initializes the >> LazyConstant. >> It just checks rather the value is init and if not calls the >> supplier to get a substitute for the missing constant. >> Example: >> LazyConstant x = LazyConstant.of(() -> "Const"); >> var uninit1 = x.orElse(() -> "substitute 1"); >> var uninit2 = x.orElse(() -> "substitute 2"); >> var init1 = x.get(); >> var init2 = x.orElse(() -> "substitute 3"); >> uninit1 and uninit2 get the substitute 1/2 >> And init1 and init2 get Const. >> >> This is surprising if you expect it to be a way to init it >> with an alternative value. >> >> My suggestion would to make the separation clear and allow >> for another use case by spliting this api in 2 parts: >> One class LazyConstant >> Takes a Supplier in static factory and exposes get() >> >> And >> Class LazyInit >> Which takes no arguments in the static factory and takes a >> supplier in the get method that gets called when get is >> called for the first time. >> In this case the source for the constant can be any piece of >> code that has access to the LazyConstant. This might be >> desired in some cases. In cases where it's not the other >> version can be used. >> >> This split makes it clear from which context the constant is >> initialized from (consumer or at declaration) >> >> Mixing those 2 or having methods that appear to do this is >> rather confusing. >> >> >> >> One solution for the "i might not want to init the constant" >> case the "orElse" method is meant to be is to have a method >> "tryGet" which returns Optional instead. This makes it clear >> that the value might not be there and is not initialized when >> calling the method. Nobody expects to init the constant when >> calling orElse on a returned Optional. >> >> My 2 suggestions here are completely independent and should >> be viewed as such. >> >> Great regards >> RedIODev >> >> On Fri, Dec 5, 2025, 13:55 david Grajales >> wrote: >> >> HI Per. I pleasure to talk with you. >> >> You are right about one thing but this actually makes the >> API less intuitive and harder to read and reason about. >> >> LazyConstant foo = LazyConstant.of(() -> "hello"); >> >> void main() { >> ? ? if (someCondition()) {// asume false >> ? ? ? ? foo.get(); >> ? ? } >> ? ? foo.orElse("hello2"); // ... >> >> ? ? println(foo.get()); // This prints "hello" >> } >> >> But if one assigns foo.orElse("hello2") to a variable, >> the variable actually gets the "hello2" value. >> >> void main() { >> ? ? if (someCondition()) {// asume false >> ? ? ? ? foo.get(); >> ? ? } >> ? ? var res = foo.orElse("hello2"); // ... >> ? ? var res2 = foo.orElse("hello3"); >> ? ? println(res); // This prints "hello2" >> ? ? println(res2);//This prints "hello3" >> } >> >> This is actually even more confusing and makes the API >> more error prone. I personally think once initialized the >> lazy constant should always return the same value (maybe >> through the .get() method only), and there should not be >> any possibility of getting a different values?from the >> same instance either in the .of() static method or in any >> hypothetical instance method for conditional downstream >> logic.? I guess one could achieve the latter with the >> static factory method through?something like this >> (although?less elegant) >> >> private class Bar{ >> ? ? private final LazyConstant foo; >> ? ? private Bar(Some some){ >> >> ? ? ? ? if(some.condition){ >> ? ? ? ? ? ? foo = LazyConstant.of(() -> "hello"); >> ? ? ? ? }else { >> ? ? ? ? ? ? foo = LazyConstant.of(() -> "hello2"); >> ? ? ? ? } >> ? ? } >> } >> >> Thank you for reading. This is all I have to report. >> >> Best regards. >> >> >> >> El vie, 5 dic 2025 a la(s) 6:05?a.m., Per-Ake Minborg >> (per-ake.minborg at oracle.com) escribi?: >> >> Hi David, >> >> Thank you for trying out LazyConstant and providing >> feedback. That is precisely what previews are for! >> >> If you take a closer look at the specification of >> |LazyConstant::orElse,|?it says that the method will >> /never trigger initialization./?And so, you >> /can/?actually be sure that in your first example, >> |foo|?is always initialized to "hello" (if ever >> initialized). It is only if foo is not initialized >> that the method will return "hello2" (again, without >> initializing foo). This is similar to how >> |Optional|?works. >> >> It would be possible to entirely remove the >> |orElse()|?method from the API, and in the rare cases >> where an equivalent functionality is called for, rely >> on |LazyConstant::isInitialized|?instead. >> >> Best, Per >> >> >> Confidential- Oracle Internal >> ------------------------------------------------------------------------ >> *From:* amber-dev on >> behalf of david Grajales >> *Sent:* Friday, December 5, 2025 5:38 AM >> *To:* amber-dev ; >> core-libs-dev at openjdk.org >> *Subject:* Feedback about LazyConstants API (JEP526) >> Dear Java Dev Team, >> >> ?I am writing to provide feedback and two specific >> observations regarding the LazyConstant API, which is >> currently a preview feature in OpenJDK 26. >> >> ?I appreciate the API's direction and I think it's a >> good improvement compared to its first iteration; >> however, I see potential for improved expressiveness, >> particularly in conditional scenarios. >> >> >> *1. Proposal: Zero-Parameter `LazyConstant.of()` >> Overload:* >> >> Currently, the mandatory use of a factory method >> receiving a `Supplier` (due to the lack of a public >> constructor) can obscure the expressiveness of >> conditional or multiple-value initialization paths. >> **The Issue:** When looking at the declaration: >> >> LazyConstant foo = LazyConstant.of(() -> >> "hello"); >> >> the code gives the strong, immediate impression that >> the value is *always* initialized to |"hello"|. This >> makes it difficult to infer that the constant might >> ultimately resolve to an alternative value set later >> via |orElse()| or another conditional path, >> especially when skimming the code: >> >> LazyConstant foo = LazyConstant.of(() -> >> "hello"); // When skimming the code it's not always >> obvious that this may not be the actual value >> void main() { >> if (someCondition()) { >> ? ? ? ? ? foo.get(); // Trigger initialization to >> "hello" >> ?} >> // If someCondition is false, the final value of foo >> is determined here: >> var res1 = foo.orElse("hello2"); // ... >> } >> >> *My Suggestion:* I propose introducing a >> *zero-parameter overloaded static factory method* |of()|: >> >> LazyConstant foo = LazyConstant.of(); >> >> This form explicitly communicates that the constant >> is initialized to an *unresolved* state, suggesting >> that the value will be determined downstream by the >> first invocation of an initialization/computation method. >> >> LazyConstant foo = LazyConstant.of(); // >> Clearly unresolved >> void main() { >> if (someCondition()) { >> ? ? ? foo.orElse("hello"); >> ?} >> var res1 = foo.orElse("hello2"); // ... >> } >> >> This is specially useful for clarity when one has >> conditional initialization in places such as the >> constructor of a class. For example >> >> private class Bar{ >> ? ? LazyConstant foo = LazyConstant.of(); >> ? ? private Bar(Some some){ >> ? ? ? ? if(some.condition()){ >> ? ? ? ? ? ? foo.orElse("foo"); >> ? ? ? ? } >> ? ? ? ? foo.orElse("foo2"); >> ? ? } >> >> ? ? String computeValue() { >> ? ? ? ? return "hello"; >> ? ? } >> >> ? ? String computeValue2(){ >> ? ? ? ? return "hello2"; >> ? ? } >> } >> >> >> 2. Method Naming Suggestion and and supplier in >> instance method for consistency in the API >> >> My second, much more minor observation relates to the >> instance method |orElse(T t)|. >> >> While |orElse| fits a retrieval pattern, I personally >> feel that *|compute|* or *|computeIfAbsent|* would >> better express the intent of this method, as its >> primary function is not just to retrieve, but to >> trigger the computation and *set the final value* of >> the constant if it is currently uninitialized. Also, >> as the factory of() has a supplier i think this >> instance method should also receive a Supplier, This >> not only keeps the API consistent in the usage but >> makes more ergonomic the declaration of complex >> initialization logic inside the method. >> >> >> private class Bar{ >> LazyConstant foo = >> LazyConstant.of(InitParam::default); // Under the >> current API this is mandatory but in reality the >> value is set in the constructor, default is never >> really used. >> ? ? private Bar(Some some){ >> ?foo.compute(some::executeCallToCacheDBAndBringInitializationParams) >> //Real configuration happens here >> >> ? ? } >> } >> >> This last it's very common for initialization of >> configuration classes and singletons. >> >> >> Thank you so much for your attention, I hope you find >> this feedback useful. >> >> Always yours. David Grajales >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From kan.izh at gmail.com Tue Dec 9 11:59:46 2025 From: kan.izh at gmail.com (Anatoly Kupriyanov) Date: Tue, 9 Dec 2025 11:59:46 +0000 Subject: [External] : Re: Feedback about LazyConstants API (JEP526) In-Reply-To: <7f79f147-c9f1-4411-94dc-81b737def634@oracle.com> References: <10add665-86f5-4a5e-9b4b-d4ceb20b4494@oracle.com> <37152b24-6f4c-4fb9-a2f8-c444d3be58e6@oracle.com> <7f79f147-c9f1-4411-94dc-81b737def634@oracle.com> Message-ID: I don't think it's a problem. To be honest, I don't really see why this method causes such confusion.. It behaves very similarly to the Optional's method of the same name. I would support the Red IO suggestion to have a `tryGet` method returning Optional as a way to tackle the confusion. If you read javadoc for orElse it says "Returns the constant if initialized, otherwise, returns other", which is quite literally what I did show in my code snippet. Logically it is the same, but you are right, from implementation perspective it uses getAcquire twice, which potentially could have performance penalty. Your code snippet is incorrect, as the `get` never throws. -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Tue Dec 9 12:05:28 2025 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Tue, 9 Dec 2025 12:05:28 +0000 Subject: [External] : Re: Feedback about LazyConstants API (JEP526) In-Reply-To: References: <10add665-86f5-4a5e-9b4b-d4ceb20b4494@oracle.com> <37152b24-6f4c-4fb9-a2f8-c444d3be58e6@oracle.com> <7f79f147-c9f1-4411-94dc-81b737def634@oracle.com> Message-ID: On 09/12/2025 11:59, Anatoly Kupriyanov wrote: > To be honest, I don't really see why this method causes such confusion. In part I agree. E.g. when we added this, what we had in mind was just https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/ScopedValue.html#orElse(T) E.g. other APIs have `orElse` method that return a user-provided value if some condition isn't met. I believe the problem we're discussing here is likely also related to the fact that the API used to have a side-effecting `orElseSet`, which is now removed, and I wonder if, because of that, folks are reading too much into what orElse does? Maurizio From kan.izh at gmail.com Tue Dec 9 12:51:14 2025 From: kan.izh at gmail.com (Anatoly Kupriyanov) Date: Tue, 9 Dec 2025 12:51:14 +0000 Subject: [External] : Re: Feedback about LazyConstants API (JEP526) In-Reply-To: References: <10add665-86f5-4a5e-9b4b-d4ceb20b4494@oracle.com> <37152b24-6f4c-4fb9-a2f8-c444d3be58e6@oracle.com> <7f79f147-c9f1-4411-94dc-81b737def634@oracle.com> Message-ID: Right, the ScopedValue is another good example I've forgotten. In that case I am even more inclined to keep the `orElse` as it looks like a repeating pattern across JDK libraries. Consistency is the way to go! And maybe even consider having a new interface for the method to make this pattern explicit?.. I am glad that `orElseSet` is removed, the side-effecting is bad; also in other parts of JDK we already have `computeIfAbsent` for the same idea. I did not hear about it, and yeah, sounds like the source of this confusion. On Tue, 9 Dec 2025 at 12:05, Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > > On 09/12/2025 11:59, Anatoly Kupriyanov wrote: > > To be honest, I don't really see why this method causes such confusion. > > In part I agree. E.g. when we added this, what we had in mind was just > > > https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/ScopedValue.html#orElse(T) > > E.g. other APIs have `orElse` method that return a user-provided value > if some condition isn't met. > > I believe the problem we're discussing here is likely also related to > the fact that the API used to have a side-effecting `orElseSet`, which > is now removed, and I wonder if, because of that, folks are reading too > much into what orElse does? > > Maurizio > > -- WBR, Anatoly. -------------- next part -------------- An HTML attachment was scrubbed... URL: From redio.development at gmail.com Tue Dec 9 13:36:09 2025 From: redio.development at gmail.com (Red IO) Date: Tue, 9 Dec 2025 14:36:09 +0100 Subject: Fwd: [External] : Re: Feedback about LazyConstants API (JEP526) In-Reply-To: References: <10add665-86f5-4a5e-9b4b-d4ceb20b4494@oracle.com> <37152b24-6f4c-4fb9-a2f8-c444d3be58e6@oracle.com> <7f79f147-c9f1-4411-94dc-81b737def634@oracle.com> Message-ID: Missed the CC ---------- Forwarded message --------- From: Red IO Date: Tue, Dec 9, 2025, 14:34 Subject: Re: [External] : Re: Feedback about LazyConstants API (JEP526) To: Anatoly Kupriyanov I initially thought I agree with your statement that orElse is a common pattern in the jdk. But then I failed to come up with a second example. I then searched the jdk github repo for the method. And I only found Optional and it's specializations and ClassHierarchyResolver. So I would suggest yes, it's an often used method ... of the Optional class. Not many apis seem to expose it. The case of exposing an accessor that returns an Optional on the other hand is incredibly common across the jdk. This is exactly the case Optional was designed for. In this sense Optional is the "Interface" you are suggesting. My main argument against reusing orElse here is that the context is a completely different one. An Optional orElse method is a pure function that always returns the same value. It signals that the value is not there. LazyConstant is different in this regard. The LazyConstant orElse is not pure at all. It depends on rather someone else already initialized the value or not. It signals that the value is not there YET. Great regards RedIODev On Tue, Dec 9, 2025, 13:51 Anatoly Kupriyanov wrote: > Right, the ScopedValue is another good example I've forgotten. In that > case I am even more inclined to keep the `orElse` as it looks like a > repeating pattern across JDK libraries. Consistency is the way to go! > And maybe even consider having a new interface for the method to make this > pattern explicit?.. > > I am glad that `orElseSet` is removed, the side-effecting is bad; also in > other parts of JDK we already have `computeIfAbsent` for the same idea. I > did not hear about it, and yeah, sounds like the source of this confusion. > > > On Tue, 9 Dec 2025 at 12:05, Maurizio Cimadamore < > maurizio.cimadamore at oracle.com> wrote: > >> >> On 09/12/2025 11:59, Anatoly Kupriyanov wrote: >> > To be honest, I don't really see why this method causes such confusion. >> >> In part I agree. E.g. when we added this, what we had in mind was just >> >> >> https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/ScopedValue.html#orElse(T) >> >> E.g. other APIs have `orElse` method that return a user-provided value >> if some condition isn't met. >> >> I believe the problem we're discussing here is likely also related to >> the fact that the API used to have a side-effecting `orElseSet`, which >> is now removed, and I wonder if, because of that, folks are reading too >> much into what orElse does? >> >> Maurizio >> >> > > -- > WBR, Anatoly. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From kan.izh at gmail.com Tue Dec 9 14:12:44 2025 From: kan.izh at gmail.com (Anatoly Kupriyanov) Date: Tue, 9 Dec 2025 14:12:44 +0000 Subject: [External] : Re: Feedback about LazyConstants API (JEP526) In-Reply-To: References: <10add665-86f5-4a5e-9b4b-d4ceb20b4494@oracle.com> <37152b24-6f4c-4fb9-a2f8-c444d3be58e6@oracle.com> <7f79f147-c9f1-4411-94dc-81b737def634@oracle.com> Message-ID: My idea is not an optional *interface*, but an interface for something which is convertible to the Optional *type*. In other words, neither the LazyConstant nor to ScopedVariable *is not* an optional itself, but could be converted to it uniformly. Something like this: interface Optionable {// need to think about the better naming! T orElse(T other); // and maybe even: default Optional asOptional() { return Optional.ofNullable(this.orElse(null)); }; } and then LazyConstant, ScopedVariable, etc could just implement the interface to unify on the notion of "return a user-provided value if some condition isn't met". Sounds like a decent path to abolish nulls. But I feel I am overthinking this... On Tue, 9 Dec 2025 at 13:35, Red IO wrote: > I initially thought I agree with your statement that orElse is a common > pattern in the jdk. But then I failed to come up with a second example. I > then searched the jdk github repo for the method. And I only found Optional > and it's specializations and ClassHierarchyResolver. > So I would suggest yes, it's an often used method ... of the Optional > class. Not many apis seem to expose it. The case of exposing an accessor > that returns an Optional on the other hand is incredibly common across the > jdk. This is exactly the case Optional was designed for. In this sense > Optional is the "Interface" you are suggesting. > > My main argument against reusing orElse here is that the context is a > completely different one. > An Optional orElse method is a pure function that always returns the same > value. It signals that the value is not there. > LazyConstant is different in this regard. The LazyConstant orElse is not > pure at all. It depends on rather someone else already initialized the > value or not. It signals that the value is not there YET. > > Great regards > RedIODev > > > > On Tue, Dec 9, 2025, 13:51 Anatoly Kupriyanov wrote: > >> Right, the ScopedValue is another good example I've forgotten. In that >> case I am even more inclined to keep the `orElse` as it looks like a >> repeating pattern across JDK libraries. Consistency is the way to go! >> And maybe even consider having a new interface for the method to make >> this pattern explicit?.. >> >> I am glad that `orElseSet` is removed, the side-effecting is bad; also in >> other parts of JDK we already have `computeIfAbsent` for the same idea. I >> did not hear about it, and yeah, sounds like the source of this confusion. >> >> >> On Tue, 9 Dec 2025 at 12:05, Maurizio Cimadamore < >> maurizio.cimadamore at oracle.com> wrote: >> >>> >>> On 09/12/2025 11:59, Anatoly Kupriyanov wrote: >>> > To be honest, I don't really see why this method causes such confusion. >>> >>> In part I agree. E.g. when we added this, what we had in mind was just >>> >>> >>> https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/ScopedValue.html#orElse(T) >>> >>> E.g. other APIs have `orElse` method that return a user-provided value >>> if some condition isn't met. >>> >>> I believe the problem we're discussing here is likely also related to >>> the fact that the API used to have a side-effecting `orElseSet`, which >>> is now removed, and I wonder if, because of that, folks are reading too >>> much into what orElse does? >>> >>> Maurizio >>> >>> >> >> -- >> WBR, Anatoly. >> > -- WBR, Anatoly. -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Tue Dec 9 14:23:22 2025 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Tue, 9 Dec 2025 14:23:22 +0000 Subject: [External] : Re: Feedback about LazyConstants API (JEP526) In-Reply-To: References: <10add665-86f5-4a5e-9b4b-d4ceb20b4494@oracle.com> <37152b24-6f4c-4fb9-a2f8-c444d3be58e6@oracle.com> <7f79f147-c9f1-4411-94dc-81b737def634@oracle.com> Message-ID: <6c9909d2-3412-4cb2-9944-80f43854bfb5@oracle.com> I believe there is definitively possible overthinking at play here. Some notes: * in any case, we have at least _two_ primitives (either orElse + get, or tryGet + get). That's because one primitives triggers initialization, the other doesn't. * orElse is similar to what we do in ScopedValue. There's not many other APIs that do this. * creating an optional is more expensive -- but with Valhalla costs will likely go down and be comparable * returning an Optional might also be confusing because the Optional is a _snapshot_ at some point in the lifecycle of the lazy constant -- e.g. you can get an empty optional with tryGet(), then call get(). Now the previously returned optional is not in sync with the lazy constant (not sure how much this is a concern -- but listing it here as a possible element of confusion) * in all possible worlds, it seems that `isInitialized` can be derived, either from orElse, or from tryGet. That said, for a construct called LazyConstant, I think asking whether the constant is initialized or not seems a good method to have, and can also be used to explain some of the behavior of the API (e.g. in the javadoc). So I'm not sure I'd be ok with going the full way and drop it. Given all this, possible way forwards would be: 1. keep everything as is (after all, we got here for a reason) 2. drop orElse, and keep just get/isInitialzied (on the basis that the internal optimization of orElse is not really worth it. We can also always add it at a later point compatibly) 3. drop orElse and replace it with tryGet or some Optional-returning equivalent I don't have strong opinions on either of these. Perhaps, the "if in doubt leave it out" angle should win (2). Maurizio On 09/12/2025 14:12, Anatoly Kupriyanov wrote: > My idea is not an optional /interface/, but an interface for something > which is convertible to the Optional /type/. In other words, neither > the LazyConstant nor to ScopedVariable *is not*?an optional itself, > but could be converted?to it uniformly. > Something like this: > > interface Optionable {// need to think about the better naming! > ? T orElse(T other); > > ? ?// and maybe even: > ?default?Optional asOptional() { > ? ? return Optional.ofNullable(this.orElse(null)); > ? ?}; > } > > and then?LazyConstant,?ScopedVariable, etc could just implement the > interface to?unify on the notion of "return a user-provided value > if some condition isn't met". Sounds like a decent path to abolish nulls. > > But I feel I am overthinking this... > > > On Tue, 9 Dec 2025 at 13:35, Red IO wrote: > > I initially thought I agree with your statement that orElse is a > common pattern in the jdk. But then I failed to come up with a > second example. I then searched the jdk github repo for the > method. And I only found Optional and it's specializations and > ClassHierarchyResolver. > So I would suggest yes, it's an often used method ... of the > Optional class. Not many apis seem to expose it. The case of > exposing an accessor that returns an Optional on the other hand is > incredibly common across the jdk. This is exactly the case > Optional was designed for. In this sense Optional is the > "Interface" you are suggesting. > > My main argument against reusing orElse here is that the context > is a completely different one. > An Optional orElse method is a pure function that always returns > the same value. It signals that the value is not there. > LazyConstant is different in this regard. The LazyConstant orElse > is not pure at all. It depends on rather someone else already > initialized the value or not. It signals that the value is not > there YET. > > Great regards > RedIODev > > > > On Tue, Dec 9, 2025, 13:51 Anatoly Kupriyanov > wrote: > > Right, the ScopedValue is another good example I've forgotten. > In that case I am even more inclined to keep the `orElse` as > it looks like a repeating pattern across JDK libraries. > Consistency is the way to go! > And maybe even consider having a new interface for?the method > to make this pattern explicit?.. > > I am glad that `orElseSet` is removed, the side-effecting is > bad; also in other parts of JDK we already have > `computeIfAbsent` for the same idea. I did not hear about it, > and yeah, sounds like the source of this confusion. > > > On Tue, 9 Dec 2025 at 12:05, Maurizio Cimadamore > wrote: > > > On 09/12/2025 11:59, Anatoly Kupriyanov wrote: > > To be honest, I don't really see why this method causes > such confusion. > > In part I agree. E.g. when we added this, what we had in > mind was just > > https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/ScopedValue.html#orElse(T) > > E.g. other APIs have `orElse` method that return a > user-provided value > if some condition isn't met. > > I believe the problem we're discussing here is likely also > related to > the fact that the API used to have a side-effecting > `orElseSet`, which > is now removed, and I wonder if, because of that, folks > are reading too > much into what orElse does? > > Maurizio > > > > -- > WBR, Anatoly. > > > > -- > WBR, Anatoly. -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.1993grajales at gmail.com Tue Dec 9 14:46:39 2025 From: david.1993grajales at gmail.com (david Grajales) Date: Tue, 9 Dec 2025 09:46:39 -0500 Subject: [External] : Re: Feedback about LazyConstants API (JEP526) In-Reply-To: References: <10add665-86f5-4a5e-9b4b-d4ceb20b4494@oracle.com> <37152b24-6f4c-4fb9-a2f8-c444d3be58e6@oracle.com> <7f79f147-c9f1-4411-94dc-81b737def634@oracle.com> Message-ID: I am glad my feedback was helpful and sparkled such a great discussion. I would like to put 2 cents more, looking for these to be helpful. My main concern with "orElse" is that most of the time (at least for my how I would use this API in my job) most of the time I need a reliable way to set the constant and use that particular constant along the life cycle of the class, **not** declaring an alternative local variable in replacement because most of the time there is no good "burned in the code" alternatives that I could use . The use cases I have for this API are usually about deferred initialization of values that often require some time costly operation, some of those may involve calls to external services (IO operations) thus having a "burned" constant in these cases is not useful. Instead I propose a "computeIfAbsent" (I am not against orElse naming) method that allows for alternative downstream conditional initialization of the Lazy constant. private class Bar{ LazyCosntan weatherUrl = LazyCosntant.of(this::checkAndGetWeatherUrl); public Bar(){} private String checkAndGetWeatherUrl(){ return Executors.newVirtualThreadPerTaskExecutor() .submit(() -> /*Some query to check if the weather server is up*/); } private String checkAndGetAltWeatherUrl(){ return Executors.newVirtualThreadPerTaskExecutor() .submit(() -> /*Some query to check if the alt weather server is up*/); } public Weather getWeather(){ var url = weatherUrl.computeIfAbsent(this::checkAndGetAltWeatherUrl).get(); // logic to get the weather here using the lazy constants// } public void sendWeather(){ var url = weatherUrl.computeIfAbsent(this::checkAndGetAltWeatherUrl).get(); Executors.newVirtualThreadPerTaskExecutor() .submit(() -> /*Send the weather url to somewhere else* using weatherUrl*/); } } This pattern is very common, either for a trivial weather or to check or a conf and alternatives in case the regular one is not available (for example a conf file that may be missing and one may set a method that downloads it from a remote server first in case it is absent) So for me the issue is not the concept of "orElse" but how the current implementation returns me an alternative value instead of SETTING an alternative value in case the regular attempt fails or hasn't been called still because the program followed an alternative path before the obvious regular initialization path. If the orElse (or any other name that fits) changes the behaviour to set a value instead of returning something it would be the best approach IMHO. El mar, 9 dic 2025 a la(s) 9:13?a.m., Anatoly Kupriyanov (kan.izh at gmail.com) escribi?: > My idea is not an optional *interface*, but an interface for something > which is convertible to the Optional *type*. In other words, neither the > LazyConstant nor to ScopedVariable *is not* an optional itself, but could > be converted to it uniformly. > Something like this: > > interface Optionable {// need to think about the better naming! > T orElse(T other); > > // and maybe even: > default Optional asOptional() { > return Optional.ofNullable(this.orElse(null)); > }; > } > > and then LazyConstant, ScopedVariable, etc could just implement the > interface to unify on the notion of "return a user-provided value > if some condition isn't met". Sounds like a decent path to abolish nulls. > > But I feel I am overthinking this... > > > On Tue, 9 Dec 2025 at 13:35, Red IO wrote: > >> I initially thought I agree with your statement that orElse is a common >> pattern in the jdk. But then I failed to come up with a second example. I >> then searched the jdk github repo for the method. And I only found Optional >> and it's specializations and ClassHierarchyResolver. >> So I would suggest yes, it's an often used method ... of the Optional >> class. Not many apis seem to expose it. The case of exposing an accessor >> that returns an Optional on the other hand is incredibly common across the >> jdk. This is exactly the case Optional was designed for. In this sense >> Optional is the "Interface" you are suggesting. >> >> My main argument against reusing orElse here is that the context is a >> completely different one. >> An Optional orElse method is a pure function that always returns the same >> value. It signals that the value is not there. >> LazyConstant is different in this regard. The LazyConstant orElse is not >> pure at all. It depends on rather someone else already initialized the >> value or not. It signals that the value is not there YET. >> >> Great regards >> RedIODev >> >> >> >> On Tue, Dec 9, 2025, 13:51 Anatoly Kupriyanov wrote: >> >>> Right, the ScopedValue is another good example I've forgotten. In that >>> case I am even more inclined to keep the `orElse` as it looks like a >>> repeating pattern across JDK libraries. Consistency is the way to go! >>> And maybe even consider having a new interface for the method to make >>> this pattern explicit?.. >>> >>> I am glad that `orElseSet` is removed, the side-effecting is bad; also >>> in other parts of JDK we already have `computeIfAbsent` for the same idea. >>> I did not hear about it, and yeah, sounds like the source of this confusion. >>> >>> >>> On Tue, 9 Dec 2025 at 12:05, Maurizio Cimadamore < >>> maurizio.cimadamore at oracle.com> wrote: >>> >>>> >>>> On 09/12/2025 11:59, Anatoly Kupriyanov wrote: >>>> > To be honest, I don't really see why this method causes such >>>> confusion. >>>> >>>> In part I agree. E.g. when we added this, what we had in mind was just >>>> >>>> >>>> https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/ScopedValue.html#orElse(T) >>>> >>>> E.g. other APIs have `orElse` method that return a user-provided value >>>> if some condition isn't met. >>>> >>>> I believe the problem we're discussing here is likely also related to >>>> the fact that the API used to have a side-effecting `orElseSet`, which >>>> is now removed, and I wonder if, because of that, folks are reading too >>>> much into what orElse does? >>>> >>>> Maurizio >>>> >>>> >>> >>> -- >>> WBR, Anatoly. >>> >> > > -- > WBR, Anatoly. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From maurizio.cimadamore at oracle.com Tue Dec 9 15:18:33 2025 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Tue, 9 Dec 2025 15:18:33 +0000 Subject: [External] : Re: Feedback about LazyConstants API (JEP526) In-Reply-To: References: <10add665-86f5-4a5e-9b4b-d4ceb20b4494@oracle.com> <37152b24-6f4c-4fb9-a2f8-c444d3be58e6@oracle.com> <7f79f147-c9f1-4411-94dc-81b737def634@oracle.com> Message-ID: <1dc510fe-f85a-432e-b258-80bd9a8f4cab@oracle.com> Hi David, I think what you suggest is outside the scope of the LazyConstant API (as Per has already said). The goal of LazyConstant is to provide an easy to use abstraction for lazy initialization that addresses the 90% use case (e.g. deferred initialization using a lambda). There will be another, more imperative API that can be used to build what you want. But what we have learned when working on this project, is that if we increase the scope/reach of LazyConstant to include more imperative aspects, it ends up with confusion pretty quickly. The design you proposed was considered -- and rejected. While it holds together, the fact that the initialization action provided at creation acts now as a "fallback" initialization action, and can be overridden by use sites calling computeIfAbsent was deemed surprising and/or confusing. Regards Maurizio On 09/12/2025 14:46, david Grajales wrote: > I am glad my feedback was helpful and sparkled such a great > discussion. I would like to put 2 cents more, looking for these to be > helpful. > > My main concern with "orElse" is that most of the?time (at least for > my how I would use this API in my job) most of the time I need a > reliable way to set the constant and use that particular constant > along the life cycle of the class, **not** declaring an alternative > local variable in replacement because most of the time there is no > good "burned in the code" alternatives? that I could use . The use > cases I have for this API are usually about deferred initialization of > values that often require some time costly operation, some of those > may involve calls to external services (IO operations) thus having a > "burned" constant in these cases is not useful. Instead I propose a > "computeIfAbsent" (I am not against orElse naming) method that allows > for alternative downstream conditional initialization of the Lazy > constant. > > private class Bar{ > LazyCosntanweatherUrl = LazyCosntant.of(this::checkAndGetWeatherUrl); > > public Bar(){} > > private String checkAndGetWeatherUrl(){ > return Executors.newVirtualThreadPerTaskExecutor() > .submit(() ->/*Some query to check if the weather server is up*/); > } > > private String checkAndGetAltWeatherUrl(){ > return Executors.newVirtualThreadPerTaskExecutor() > .submit(() ->/*Some query to check if the alt weather server is up*/); > } > > public WeathergetWeather(){ > > var url =weatherUrl.computeIfAbsent(this::checkAndGetAltWeatherUrl).get(); > // logic to get the weather here using the lazy constants// } > public voidsendWeather(){ > var url =weatherUrl.computeIfAbsent(this::checkAndGetAltWeatherUrl).get(); > Executors.newVirtualThreadPerTaskExecutor() > .submit(() ->/*Send the weather url to somewhere else* using weatherUrl*/); > } > } > > This pattern is very common, either for a trivial weather or to check > or a conf and alternatives in case the regular one is not available > (for example a conf file that may be missing and one may set a method > that downloads it from a remote server first in case it is absent) > > So for me the issue is not the concept of "orElse" but how the current > implementation returns me an alternative value instead of SETTING an > alternative value in case the regular attempt fails or hasn't been > called still because the program followed an alternative path before > the obvious regular initialization path. If the orElse (or any other > name that fits) changes the behaviour to set a value instead of > returning something it would be the best approach IMHO. > > > > > El mar, 9 dic 2025 a la(s) 9:13?a.m., Anatoly Kupriyanov > (kan.izh at gmail.com) escribi?: > > My idea is not an optional /interface/, but an interface for > something which is convertible to the Optional /type/. In other > words, neither the LazyConstant nor to ScopedVariable *is not*?an > optional itself, but could be converted?to it uniformly. > Something like this: > > interface Optionable {// need to think about the better naming! > ? T orElse(T other); > > ? ?// and maybe even: > ?default?Optional asOptional() { > ? ? return Optional.ofNullable(this.orElse(null)); > ? ?}; > } > > and then?LazyConstant,?ScopedVariable, etc could just implement > the interface to?unify on the notion of "return a user-provided value > if some condition isn't met". Sounds like a decent path to abolish > nulls. > > But I feel I am overthinking this... > > > On Tue, 9 Dec 2025 at 13:35, Red IO > wrote: > > I initially thought I agree with your statement that orElse is > a common pattern in the jdk. But then I failed to come up with > a second example. I then searched the jdk github repo for the > method. And I only found Optional and it's specializations and > ClassHierarchyResolver. > So I would suggest yes, it's an often used method ... of the > Optional class. Not many apis seem to expose it. The case of > exposing an accessor that returns an Optional on the other > hand is incredibly common across the jdk. This is exactly the > case Optional was designed for. In this sense Optional is the > "Interface" you are suggesting. > > My main argument against reusing orElse here is that the > context is a completely different one. > An Optional orElse method is a pure function that always > returns the same value. It signals that the value is not there. > LazyConstant is different in this regard. The LazyConstant > orElse is not pure at all. It depends on rather someone else > already initialized the value or not. It signals that the > value is not there YET. > > Great regards > RedIODev > > > > On Tue, Dec 9, 2025, 13:51 Anatoly Kupriyanov > wrote: > > Right, the ScopedValue is another good example I've > forgotten. In that case I am even more inclined to keep > the `orElse` as it looks like a repeating pattern across > JDK libraries. Consistency is the way to go! > And maybe even consider having a new interface for?the > method to make this pattern explicit?.. > > I am glad that `orElseSet` is removed, the side-effecting > is bad; also in other parts of JDK we already have > `computeIfAbsent` for the same idea. I did not hear about > it, and yeah, sounds like the source of this confusion. > > > On Tue, 9 Dec 2025 at 12:05, Maurizio Cimadamore > wrote: > > > On 09/12/2025 11:59, Anatoly Kupriyanov wrote: > > To be honest, I don't really see why this method > causes such confusion. > > In part I agree. E.g. when we added this, what we had > in mind was just > > https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/ScopedValue.html#orElse(T) > > E.g. other APIs have `orElse` method that return a > user-provided value > if some condition isn't met. > > I believe the problem we're discussing here is likely > also related to > the fact that the API used to have a side-effecting > `orElseSet`, which > is now removed, and I wonder if, because of that, > folks are reading too > much into what orElse does? > > Maurizio > > > > -- > WBR, Anatoly. > > > > -- > WBR, Anatoly. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.1993grajales at gmail.com Tue Dec 9 15:23:43 2025 From: david.1993grajales at gmail.com (david Grajales) Date: Tue, 9 Dec 2025 10:23:43 -0500 Subject: [External] : Re: Feedback about LazyConstants API (JEP526) In-Reply-To: <1dc510fe-f85a-432e-b258-80bd9a8f4cab@oracle.com> References: <10add665-86f5-4a5e-9b4b-d4ceb20b4494@oracle.com> <37152b24-6f4c-4fb9-a2f8-c444d3be58e6@oracle.com> <7f79f147-c9f1-4411-94dc-81b737def634@oracle.com> <1dc510fe-f85a-432e-b258-80bd9a8f4cab@oracle.com> Message-ID: Hi Maurizio. Understandable. Thank you for the clarification. Best regards. El mar, 9 de dic de 2025, 10:18?a.m., Maurizio Cimadamore < maurizio.cimadamore at oracle.com> escribi?: > Hi David, > I think what you suggest is outside the scope of the LazyConstant API (as > Per has already said). > > The goal of LazyConstant is to provide an easy to use abstraction for lazy > initialization that addresses the 90% use case (e.g. deferred > initialization using a lambda). > > There will be another, more imperative API that can be used to build what > you want. > > But what we have learned when working on this project, is that if we > increase the scope/reach of LazyConstant to include more imperative > aspects, it ends up with confusion pretty quickly. > > The design you proposed was considered -- and rejected. While it holds > together, the fact that the initialization action provided at creation acts > now as a "fallback" initialization action, and can be overridden by use > sites calling computeIfAbsent was deemed surprising and/or confusing. > > Regards > Maurizio > On 09/12/2025 14:46, david Grajales wrote: > > I am glad my feedback was helpful and sparkled such a great discussion. I > would like to put 2 cents more, looking for these to be helpful. > > My main concern with "orElse" is that most of the time (at least for my > how I would use this API in my job) most of the time I need a reliable way > to set the constant and use that particular constant along the life cycle > of the class, **not** declaring an alternative local variable in > replacement because most of the time there is no good "burned in the code" > alternatives that I could use . The use cases I have for this API are > usually about deferred initialization of values that often require some > time costly operation, some of those may involve calls to external services > (IO operations) thus having a "burned" constant in these cases is not > useful. Instead I propose a "computeIfAbsent" (I am not against orElse > naming) method that allows for alternative downstream conditional > initialization of the Lazy constant. > > private class Bar{ > LazyCosntan weatherUrl = LazyCosntant.of(this::checkAndGetWeatherUrl); > > > public Bar(){} > > private String checkAndGetWeatherUrl(){ > return Executors.newVirtualThreadPerTaskExecutor() > .submit(() -> /*Some query to check if the weather server is up*/); > } > > private String checkAndGetAltWeatherUrl(){ > return Executors.newVirtualThreadPerTaskExecutor() > .submit(() -> /*Some query to check if the alt weather server is up*/); > } > > public Weather getWeather(){ > > var url = weatherUrl.computeIfAbsent(this::checkAndGetAltWeatherUrl).get(); > > // logic to get the weather here using the lazy constants// } > > public void sendWeather(){ > > var url = weatherUrl.computeIfAbsent(this::checkAndGetAltWeatherUrl).get(); > > Executors.newVirtualThreadPerTaskExecutor() > .submit(() -> /*Send the weather url to somewhere else* using weatherUrl*/); > } > > > } > > > This pattern is very common, either for a trivial weather or to check or a > conf and alternatives in case the regular one is not available (for example > a conf file that may be missing and one may set a method that downloads it > from a remote server first in case it is absent) > > So for me the issue is not the concept of "orElse" but how the current > implementation returns me an alternative value instead of SETTING an > alternative value in case the regular attempt fails or hasn't been called > still because the program followed an alternative path before the obvious > regular initialization path. If the orElse (or any other name that fits) > changes the behaviour to set a value instead of returning something it > would be the best approach IMHO. > > > > > El mar, 9 dic 2025 a la(s) 9:13?a.m., Anatoly Kupriyanov ( > kan.izh at gmail.com) escribi?: > >> My idea is not an optional *interface*, but an interface for something >> which is convertible to the Optional *type*. In other words, neither the >> LazyConstant nor to ScopedVariable *is not* an optional itself, but >> could be converted to it uniformly. >> Something like this: >> >> interface Optionable {// need to think about the better naming! >> T orElse(T other); >> >> // and maybe even: >> default Optional asOptional() { >> return Optional.ofNullable(this.orElse(null)); >> }; >> } >> >> and then LazyConstant, ScopedVariable, etc could just implement the >> interface to unify on the notion of "return a user-provided value >> if some condition isn't met". Sounds like a decent path to abolish nulls. >> >> But I feel I am overthinking this... >> >> >> On Tue, 9 Dec 2025 at 13:35, Red IO wrote: >> >>> I initially thought I agree with your statement that orElse is a common >>> pattern in the jdk. But then I failed to come up with a second example. I >>> then searched the jdk github repo for the method. And I only found Optional >>> and it's specializations and ClassHierarchyResolver. >>> So I would suggest yes, it's an often used method ... of the Optional >>> class. Not many apis seem to expose it. The case of exposing an accessor >>> that returns an Optional on the other hand is incredibly common across the >>> jdk. This is exactly the case Optional was designed for. In this sense >>> Optional is the "Interface" you are suggesting. >>> >>> My main argument against reusing orElse here is that the context is a >>> completely different one. >>> An Optional orElse method is a pure function that always returns the >>> same value. It signals that the value is not there. >>> LazyConstant is different in this regard. The LazyConstant orElse is not >>> pure at all. It depends on rather someone else already initialized the >>> value or not. It signals that the value is not there YET. >>> >>> Great regards >>> RedIODev >>> >>> >>> >>> On Tue, Dec 9, 2025, 13:51 Anatoly Kupriyanov wrote: >>> >>>> Right, the ScopedValue is another good example I've forgotten. In that >>>> case I am even more inclined to keep the `orElse` as it looks like a >>>> repeating pattern across JDK libraries. Consistency is the way to go! >>>> And maybe even consider having a new interface for the method to make >>>> this pattern explicit?.. >>>> >>>> I am glad that `orElseSet` is removed, the side-effecting is bad; also >>>> in other parts of JDK we already have `computeIfAbsent` for the same idea. >>>> I did not hear about it, and yeah, sounds like the source of this confusion. >>>> >>>> >>>> On Tue, 9 Dec 2025 at 12:05, Maurizio Cimadamore < >>>> maurizio.cimadamore at oracle.com> wrote: >>>> >>>>> >>>>> On 09/12/2025 11:59, Anatoly Kupriyanov wrote: >>>>> > To be honest, I don't really see why this method causes such >>>>> confusion. >>>>> >>>>> In part I agree. E.g. when we added this, what we had in mind was just >>>>> >>>>> >>>>> https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/lang/ScopedValue.html#orElse(T) >>>>> >>>>> E.g. other APIs have `orElse` method that return a user-provided value >>>>> if some condition isn't met. >>>>> >>>>> I believe the problem we're discussing here is likely also related to >>>>> the fact that the API used to have a side-effecting `orElseSet`, which >>>>> is now removed, and I wonder if, because of that, folks are reading >>>>> too >>>>> much into what orElse does? >>>>> >>>>> Maurizio >>>>> >>>>> >>>> >>>> -- >>>> WBR, Anatoly. >>>> >>> >> >> -- >> WBR, Anatoly. >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: