Preview
Third Preview of Lazy Constants (JEP 531).
Programs can only use LazyConstant when preview features are enabled.
Preview features may be removed in a future release,
or upgraded to permanent features of the Java platform.
A lazy constant is created using the factory method
LazyConstant.of(<computing function>).
When created, the lazy constant is not initialized, meaning it has no content.
The lazy constant (of type T) can then be initialized
(and its content retrieved) by calling get(). The first time
get() is called, the underlying computing function
(provided at construction) will be invoked and the result will be used to initialize
the constant.
Once a lazy constant is initialized, its content can never change and will always be returned by subsequent get() invocations.
Consider the following example where a lazy constant field "logger" holds
an object of type Logger:
public class Component { // Creates a new uninitialized lazy constant private final LazyConstant<Logger> logger = LazyConstant.of( () -> Logger.create(Component.class) ); public void process() { logger.get().info("Process started"); // ... } }
Initially, the lazy constant is not initialized. When logger.get()
is first invoked, it evaluates the computing function and initializes the constant to
the result; the result is then returned to the client. Hence, get()
guarantees that the constant is initialized before it returns, barring
any exceptions.
Furthermore, get() guarantees that, out of several threads trying to
invoke the computing function simultaneously, only one is
ever selected for computation. This property is crucial as evaluation of the computing
function may have side effects, for example, the call above to Logger.create()
may result in storage resources being prepared.
All failures are handled in this way. There are two special cases that cause unchecked exceptions to be thrown:
If the computing function returns null, a NoSuchElementException
(with a NullPointerException as a cause) will be thrown. Hence, a
lazy constant can never hold a null value. Clients who want to use a nullable
constant can wrap the value into an Optional holder.
If the computing function recursively invokes itself via the lazy constant, a NoSuchElementException (with an IllegalStateException as a cause) will be thrown.
Foo and a Bar
instance (that is dependent on the Foo instance) are lazily created, both of
which are held by lazy constants:
public static class Foo { // ... } public static class Bar { public Bar(Foo foo) { // ... } } static final LazyConstant<Foo> FOO = LazyConstant.of( Foo::new ); static final LazyConstant<Bar> BAR = LazyConstant.of( () -> new Bar(FOO.get()) ); public static Foo foo() { return FOO.get(); } public static Bar bar() { return BAR.get(); }
BAR.get() will create the Bar singleton if it is not already
created. Upon such a creation, a dependent Foo will first be created if
the Foo does not already exist.
The invocation of the computing function and the resulting initialization of
the constant happens-before
the initialized constant's content is read. Hence, the initialized constant's content,
including any final fields of any newly created objects, is safely published.
As subsequent retrieval of the content might be elided, there are no other memory
ordering or visibility guarantees provided as a consequence of calling
get() again.
Thread interruption does not cancel the initialization of a lazy constant. In other
words, if the computing thread is interrupted, LazyConstant::get doesn't clear
the interrupted thread’s status, nor does it throw an InterruptedException.
If the computing function blocks indefinitely, other threads operating on this lazy constant may block indefinitely; no timeouts or cancellations are provided.
static final field
to a lazy constant or if there is a chain from a static final field -- via one
or more trusted fields (i.e., static final fields,
record fields, or final instance fields in hidden classes) --
to a lazy constant.
API Note
Once a lazy constant is initialized, its content can't be removed. This can be a source of an unintended memory leak. More specifically, a lazy constant strongly references its content. Hence, the content of a lazy constant will be reachable as long as the lazy constant itself is reachable.
While it's possible to store an array inside a lazy constant, doing so will not result in improved access performance of the array elements. Instead, a lazy list of arbitrary depth can be used, which provides constant components.
The LazyConstant type is not Serializable.
Use in static initializers may interact with class initialization order; cyclic initialization may result in initialization errors as described in section 12.4 of The Java Language Specification.
Implementation Note
A lazy constant is free to synchronize on itself. Hence, care must be taken when directly or indirectly synchronizing on a lazy constant. A lazy constant is unmodifiable but its content may or may not be immutable (e.g., it may hold an ArrayList).
Optional, Supplier, List#ofLazy(int, IntFunction), Map#ofLazy(Set, Function)
| Modifier and Type | Method and Description |
|---|---|
| public boolean | |
| public T | Returns: the initialized content of this constant, computing it if necessaryRedeclares java. Returns the initialized content of this constant, computing it if necessary. |
| public int | Returns: the identity hash code for this lazy constantReturns the identity hash code for this lazy constant. |
| public static < type of the constant T> LazyConstant | Returns: a new lazy constant whose content is to be computed later via the providedcomputingFunctionin the form of a Supplier to be used
to initialize the constant computingFunction)Returns a new lazy constant whose content is to be computed later via the
provided |
| public String |
| equals | back to summary |
|---|---|
| public boolean equals(Object obj) Returns
In other words, equals compares the identity of this lazy constant and This method never triggers initialization of this lazy constant.
| |
| get | back to summary |
|---|---|
| public T get() Redeclares java. Returns the initialized content of this constant, computing it if necessary. If this constant is not initialized, first computes and initializes it using the computing function. After this method returns successfully, the constant is guaranteed to be initialized.
If an unchecked exception is thrown when evaluating the computing function or if
the computing function returns
| |
| hashCode | back to summary |
|---|---|
| public int hashCode() Returns the identity hash code for this lazy constant. This method never triggers initialization of this lazy constant.
| |
| of | back to summary |
|---|---|
| public static <T> LazyConstant Returns a new lazy constant whose content is to be computed later via the
provided
The returned lazy constant strongly references the provided
By design, the method always returns a new lazy constant even if the provided
computing function is already an instance of static <T> LazyConstant<T> ofFlattened(Supplier<? extends T> computingFunction) { return (computingFunction instanceof LazyConstant<? extends T> lc) ? (LazyConstant<T>) lc // unchecked cast is safe under normal generic usage : LazyConstant.of(computingFunction); } Implementation Note after the computing function completes (regardless of whether it succeeds or throws an unchecked exception), the computing function is no longer strongly referenced and becomes eligible for garbage collection.
| |
| toString | back to summary |
|---|---|
| public String toString() Returns a string suitable for debugging. This method never triggers initialization of this lazy constant and will observe initialization by other threads atomically (i.e., it observes the content if and only if the initialization has already completed). If this lazy constant is initialized, an implementation-dependent string containing the Object#toString() of the content will be returned; otherwise, an implementation-dependent string is returned that indicates this lazy constant is not yet initialized. | |