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