[External] : Re: Feedback about LazyConstants API (JEP526)

Anatoly Kupriyanov kan.izh at gmail.com
Tue Dec 9 11:15:03 UTC 2025


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 <davidalayachew at gmail.com>
> <davidalayachew at gmail.com>
> *Sent:* Friday, December 5, 2025 2:51 PM
> *To:* Red IO <redio.development at gmail.com> <redio.development at gmail.com>
> *Cc:* david Grajales <david.1993grajales at gmail.com>
> <david.1993grajales at gmail.com>; Per-Ake Minborg
> <per-ake.minborg at oracle.com> <per-ake.minborg at oracle.com>; amber-dev
> <amber-dev at openjdk.org> <amber-dev at openjdk.org>; core-libs-dev
> <core-libs-dev at openjdk.org> <core-libs-dev at openjdk.org>
> *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 <redio.development at gmail.com> 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<String> 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 <david.1993grajales at gmail.com>
> 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<String> 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<String> 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 <amber-dev-retn at openjdk.org> on behalf of david
> Grajales <david.1993grajales at gmail.com>
> *Sent:* Friday, December 5, 2025 5:38 AM
> *To:* amber-dev <amber-dev at openjdk.org>; 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<String> 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<String> 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<String> 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<String> 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<String> 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<InitParams> 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: <https://mail.openjdk.org/pipermail/amber-dev/attachments/20251209/48d37497/attachment-0001.htm>


More information about the amber-dev mailing list