bye stable values, enter lazy constants

Maurizio Cimadamore maurizio.cimadamore at oracle.com
Tue Sep 30 09:27:58 UTC 2025


Hi Sergey
>
> As you mentioned, something like LazyInitOnce might be a better name. 
> Is "__Constant
>  really a good alternative? Does it mainly reflect the fact that 
> @Stable is used internally, and the JVM will treat the value as a 
> ***true constants***? That seems more about internal JVM behavior than 
> the actual semantic property of being stable.
This is a valid argument -- we'll keep fishing to see if there's better 
names to express the "stability" part of the contract.
> No, I would like a solution for singletons that avoids the following 
> issues:
This is a good shopping list, thanks
>  - Uninitialized default value (e.g., LazyConstant.of()) - similar 
> problems exist with DCL
In the new API, LazyConstant.of() is no longer present (unlike 
StableValue::of), so I don't think this is an issue (e.g. you _have_ to 
pass a lambda).
>  - Value being created too early((e.g., 
> LazyConstant.of(Supplier<T>..))) and accessed without using the getter 
> method

By "value" you mean the LazyConstant itself? Or the lambda expression 
passed to the factory? Or something else?

Also, the "accessed w/o using the getter method" part: if the 
initialization logic is fully provided at construction, via the lambda, 
the only thing you can do to get the contents of a lazy constant is by 
calling its `get` method. This is no longer like StableValue, there's no 
set method. But perhaps this is the "encapsulation" problem you are 
referring to -- e.g. the fact that a lazy constant field is available in 
the namespace of the class (even if privately) in the first place. Let's 
go back to loggers:

```
class Foo {
     private static final LazyConstant<Logger> LOGGER = 
LazyConstant.of(() -> makeLogger(Foo.class));

     static Logger getLogger() {
         return LOGGER.get();
     }
}
```

The "problem" here is that you have a LOGGER field, and the 
implementation of Foo can access that field directly, w/o calling 
`getLogger`. Fine. But what can you do with LOGGER if not calling its 
get() method (which is also what the getter does) ? I'd like to 
understand the concern better here.

>  - Handling exceptions more robustly than the Holder idiom

If an exception is thrown while computing the value is never set on the 
lazy constant -- meaning the next call to `get` will re-generate the 
same exception. With holder idiom one of the issue is that exceptions 
are thrown during initialization of the holder class, which can 
sometimes add confusion.

Maurizio


More information about the leyden-dev mailing list