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