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<Weather>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 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
([email protected]) 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<T> {// need to think about the better naming!
T orElse(T other);
// and maybe even:
default Optional<T> 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 <[email protected]>
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
<[email protected]> 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
<[email protected]> 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.