<div dir="ltr">Dear Java Dev Team, <div><br></div><div> I am writing to provide feedback and two specific observations regarding the LazyConstant API, which is currently a preview feature in OpenJDK 26. </div><div><br></div><div> 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.</div><div><br></div><div><br></div><div><b>1. Proposal: Zero-Parameter `LazyConstant.of()` Overload:</b> </div><div><br></div><div>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:</div><div><br></div><div>LazyConstant<String> foo = LazyConstant.of(() -> "hello");</div><div><br></div><div>the code gives the strong, immediate impression that the value is <b>always</b> initialized to <code>"hello"</code>. This makes it difficult to infer that the constant might ultimately resolve to an alternative value set later via <code>orElse()</code> or another conditional path, especially when skimming the code:</div><div><br></div><div>LazyConstant<String> foo = LazyConstant.of(() -> <span class="gmail-hljs-string">"hello"</span>); // When skimming the code it's not always obvious that this may not be the actual value </div><div> </div><div><span class="gmail-hljs-function"><span class="gmail-hljs-keyword">void</span> <span class="gmail-hljs-title">main</span><span class="gmail-hljs-params">()</span> </span>{ </div><div> <span class="gmail-hljs-keyword">if</span> (someCondition()) { </div><div> foo.get(); <span class="gmail-hljs-comment">// Trigger initialization to "hello"</span> </div><div> } </div><div> <span class="gmail-hljs-comment">// If someCondition is false, the final value of foo is determined here:</span> </div><div> <span class="gmail-hljs-keyword">var</span> res1 = foo.orElse(<span class="gmail-hljs-string">"hello2"</span>);
<span class="gmail-hljs-comment">// ...</span> </div><div>}</div><div><br></div><div><b>My Suggestion:</b>
I propose introducing a <b>zero-parameter overloaded static factory method</b> <code>of()</code>:</div><div><br></div><div>LazyConstant<String> foo = LazyConstant.of();</div><div><br></div><div>This form explicitly communicates that the constant is initialized to an <b>unresolved</b> state, suggesting that the value will be determined downstream by the first invocation of an initialization/computation method.</div><div><br></div><div>LazyConstant<String> foo = LazyConstant.of(); <span class="gmail-hljs-comment">// Clearly unresolved</span> </div><div>
<span class="gmail-hljs-function"><span class="gmail-hljs-keyword">void</span> <span class="gmail-hljs-title">main</span><span class="gmail-hljs-params">()</span> </span>{ </div><div> <span class="gmail-hljs-keyword">if</span> (someCondition()) { </div><div> foo.orElse(<span class="gmail-hljs-string">"hello"</span>); </div><div> } </div><div> <span class="gmail-hljs-keyword">var</span> res1 = foo.orElse(<span class="gmail-hljs-string">"hello2"</span>);
<span class="gmail-hljs-comment">// ...</span> </div><div>}</div><div><br></div><div>This is specially useful for clarity when one has conditional initialization in places such as the constructor of a class. For example</div><div><br></div><div><p>private class Bar{<br> LazyConstant<String> foo = LazyConstant.of();<br> private Bar(Some some){<br> if(some.condition()){<br> foo.orElse("foo");<br> }<br> foo.orElse("foo2");<br> }<br><br> String computeValue() {<br> return "hello";<br> }<br><br> String computeValue2(){<br> return "hello2";<br> }<br>}</p></div><div><h3>2. Method Naming Suggestion and and supplier in instance method for consistency in the API</h3><p>My second, much more minor observation relates to the instance method <code>orElse(T t)</code>.</p><p>While <code>orElse</code> fits a retrieval pattern, I personally feel that <b><code>compute</code></b> or <b><code>computeIfAbsent</code></b> would better express the intent of this method, as its primary function is not just to retrieve, but to trigger the computation and <b>set the final value</b> 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.</p><p><br></p><p></p><p></p><p></p><p>private class Bar{<br> 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.<br> private Bar(Some some){<br> foo.compute(some::executeCallToCacheDBAndBringInitializationParams) //Real configuration happens here</p><p> }<br>}</p><p>This last it's very common for initialization of configuration classes and singletons.</p><p><br></p><p>Thank you so much for your attention, I hope you find this feedback useful.</p><p>Always yours. David Grajales</p></div></div>