bye stable values, enter lazy constants
Sergey Bylokhov
bylokhov at amazon.com
Tue Sep 30 00:21:06 UTC 2025
On 9/29/25 14:08, Maurizio Cimadamore wrote:
> Record components are not "lazy" -- they are assigned at record creation. Also, record is a language
> feature. Sure there's an API as well (Record) but it's very unlikely for users to use that directly.
> So I'm not sure how much of a precedent that is.
Sure, records are not lazy, that is an obvious difference between records and lazy constants. But
the similarity lies in how the 'only once' assignment is documented: when an internal field is
assigned, it can never be changed for both.
> The fact that, to get perfect runtime performance you need final on the declaration of the
> LazyConstant field seems, frankly, secondary -- and derives from the choice of modelling this as an
> API.
This might become a new trick question in interviews - similar to why the DCL pattern does not work
as expected. Ouch... "volatile", now it will be ouuch "final".
> Aside from that reference, I don't think the JEP is so focussed on performance and low-level details
> as you seem to imply? The only reference to constant folding happens quite late in the game:
Performance actually mentioned right in the Summary block of the JEP (true constants!?):
>Summary
>Introduce an API for lazy constants, which are objects that hold immutable data. Lazy constants
are treated as true constants by the JVM, enabling the same performance optimizations that are
enabled by declaring a field final. Compared to final fields, however, lazy constants offer greater
flexibility as to the timing of their initialization.
>
> > There is, furthermore, mechanical sympathy between lazy constants and the Java runtime. Under the
> hood, the content of a lazy constant is stored in a non-|final| field annotated with the JDK-
> internal |@Stable| <https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/jdk/
> internal/vm/annotation/Stable.java#L30-L90> annotation. This annotation is a common feature of low-
> level JDK code.
Right, maybe the issue is that this effort started as an attempt to expose the @Stable annotation in
the public API. Instead, perhaps the focus should have been on lazy initialization and
singleton-like behavior for various types, with performance benefits being a positive side effect,
rather than the primary goal.
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.
> I was replying to your own performance observation that LazyConstant and holder classes are the
> same. The answer is, I think, it depends. There will be cases where you have completely unrelated
> fields, and so you don't want to model these fields with a list/map -- there will be other cases
> where your class has 42 similar method handles, one for each entity it wants to represent -- at
> which point the advantage of representing each field separately seems much less obvious. The
> LazyConstant API gives you both options.
My original point was about the statement that 'a great and useful property of this idiom is that
the enclosing class's namespace is not polluted with unnecessary fields'. That property is lost if
the new API is used instead. I only brought up performance because you mentioned it:
> cretaing a _whole new class_ to avoid a class-level field, doesn't seem a great trade off.
> You need to define what you mean by "encapsulation" here -- we're talking about private fields in a
> class after all, so it's not entirely clear to me what do you mean by "expose" and "encapsulation".
‘Encapsulation’ means exactly what it says. While nested interfaces have been supported for a long
time, local interfaces were introduced to further narrow their visibility. It is just a good thing
to minimize visibility if possible.
> I believe what you are after is a way to write the field declaration as regular field declaration
> (perhaps with a keyword sprinkled on top), and let the compiler worry about desugaring the code in a
> way that doesn't need an holder class per field.
No, I would like a solution for singletons that avoids the following issues:
- Uninitialized default value (e.g., LazyConstant.of()) - similar problems exist with DCL
- Value being created too early((e.g., LazyConstant.of(Supplier<T>..))) and accessed without using
the getter method
- Handling exceptions more robustly than the Holder idiom
BTW, I think this will be one of the best features added in a long time. Hope it will be possible to
use/test it in JDK soon.
--
Best regards, Sergey.
More information about the leyden-dev
mailing list