/* * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package java.lang; import jdk.internal.javac.PreviewFeature; import jdk.internal.lang.LazyConstantImpl; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.function.IntFunction; import java.util.function.Supplier; /** * A lazy constant is a holder of content that can be initialized at most once. *

* A lazy constant is created using the factory method * {@linkplain LazyConstant#of(Supplier) LazyConstant.of({@code })}. *

* When created, the lazy constant is not initialized, meaning it has no content. *

* The lazy constant (of type {@code T}) can then be initialized * (and its content retrieved) by calling {@linkplain #get() get()}. The first time * {@linkplain #get() 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 {@linkplain #get() get()} invocations. *

* Consider the following example where a lazy constant field "{@code logger}" holds * an object of type {@code Logger}: * * {@snippet lang = java: * public class Component { * * // Creates a new uninitialized lazy constant * private final LazyConstant logger = * // @link substring="of" target="#of" : * LazyConstant.of( () -> Logger.create(Component.class) ); * * public void process() { * logger.get().info("Process started"); * // ... * } * } * } *

* Initially, the lazy constant is not initialized. When {@code 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, {@linkplain #get() get()} * guarantees that the constant is initialized before it returns, barring * any exceptions. *

* Furthermore, {@linkplain #get() get()} guarantees that, out of several threads trying to * invoke the computing function simultaneously, {@linkplain ##thread-safety 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 {@code Logger.create()} * may result in storage resources being prepared. * *

Exception handling

* If evaluation of the computing function throws an unchecked exception (i.e., a runtime * exception or an error), the lazy constant is not initialized but instead transitions to * an error state whereafter a {@linkplain NoSuchElementException} is thrown with the * unchecked exception as a cause. Subsequent {@linkplain #get() get()} calls throw * {@linkplain NoSuchElementException} (without ever invoking the computing function * again) with no cause and with a message that includes the name of the original * unchecked exception's class. *

* All failures are handled in this way. There are two special cases that cause unchecked * exceptions to be thrown: *

* If the computing function returns {@code null}, a {@linkplain NoSuchElementException} * (with a {@linkplain NullPointerException} as a cause) will be thrown. Hence, a * lazy constant can never hold a {@code null} value. Clients who want to use a nullable * constant can wrap the value into an {@linkplain Optional} holder. *

* If the computing function recursively invokes itself via the lazy constant, a * {@linkplain NoSuchElementException} (with an {@linkplain IllegalStateException} as a * cause) will be thrown. * *

Composing lazy constants

* A lazy constant can depend on other lazy constants, forming a dependency graph * that can be lazily computed but where access to individual elements can still be * performant. In the following example, a single {@code Foo} and a {@code Bar} * instance (that is dependent on the {@code Foo} instance) are lazily created, both of * which are held by lazy constants: * * {@snippet lang = java: * public static class Foo { * // ... * } * * public static class Bar { * public Bar(Foo foo) { * // ... * } * } * * static final LazyConstant FOO = LazyConstant.of( Foo::new ); * static final LazyConstant BAR = LazyConstant.of( () -> new Bar(FOO.get()) ); * * public static Foo foo() { * return FOO.get(); * } * * public static Bar bar() { * return BAR.get(); * } * } * Calling {@code BAR.get()} will create the {@code Bar} singleton if it is not already * created. Upon such a creation, a dependent {@code Foo} will first be created if * the {@code Foo} does not already exist. * *

Thread Safety

* A lazy constant is guaranteed to be initialized atomically and at most once. If * competing threads are racing to initialize a lazy constant, only one updating thread * runs the computing function (which runs on the caller's thread and is hereafter denoted * the computing thread), while the other threads are blocked until the constant * is initialized (or computation fails), after which the other threads observe the lazy * constant is initialized (or has transisioned to an error state) and leave the constant * unchanged and will never invoke any computation. *

* The invocation of the computing function and the resulting initialization of * the constant {@linkplain java.util.concurrent##MemoryVisibility happens-before} * the initialized constant's content is read. Hence, the initialized constant's content, * including any {@code 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 * {@linkplain #get()} again. *

* Thread interruption does not cancel the initialization of a lazy constant. In other * words, if the computing thread is interrupted, {@code LazyConstant::get} doesn't clear * the interrupted thread’s status, nor does it throw an {@linkplain InterruptedException}. *

* If the computing function blocks indefinitely, other threads operating on this * lazy constant may block indefinitely; no timeouts or cancellations are provided. * *

Performance

* The content of a lazy constant can never change after the lazy constant has been * initialized. Therefore, a JVM implementation may, for an initialized lazy constant, * elide all future reads of that lazy constant's content and instead use the content * that has been previously observed. We call this optimization constant folding. * This is only possible if there is a direct reference from a {@code static final} field * to a lazy constant or if there is a chain from a {@code static final} field -- via one * or more trusted fields (i.e., {@code static final} fields, * {@linkplain Record record} fields, or final instance fields in hidden classes) -- * to a lazy constant. * * @apiNote 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 {@linkplain java.lang.ref##reachability 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 * {@linkplain List#ofLazy(int, IntFunction) lazy list} of arbitrary depth can * be used, which provides constant components. *

* The {@code LazyConstant} type is not {@link Serializable}. *

* Use in static initializers may interact with class initialization order; * cyclic initialization may result in initialization errors as described * in section {@jls 12.4} of The Java Language Specification. * * @implNote * 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 {@linkplain ArrayList}). * * @param type of the constant * * @since 26 * * @see Optional * @see Supplier * @see List#ofLazy(int, IntFunction) * @see Map#ofLazy(Set, Function) * @jls 12.4 Initialization of Classes and Interfaces * @jls 17.4.5 Happens-before Order */ @PreviewFeature(feature = PreviewFeature.Feature.LAZY_CONSTANTS) public sealed interface LazyConstant extends Supplier permits LazyConstantImpl { /** * {@return 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 {@code null}, this lazy constant is not initialized * but transitions to an error state whereafter a {@linkplain NoSuchElementException} * is thrown as described in the {@linkplain ##exception-handling Exception handling} * section. * * @throws NoSuchElementException if this lazy constant is in an error state */ T get(); // Object methods /** * {@return {@code true} if this lazy constant is the same instance as * the provided {@code obj}, otherwise {@code false}} *

* In other words, equals compares the identity of this lazy constant and {@code obj} * to determine equality. Hence, two distinct lazy constants with the same content are * not equal. *

* This method never triggers initialization of this lazy constant. */ @Override boolean equals(Object obj); /** * {@return the {@linkplain System#identityHashCode(Object) identity hash code} for * this lazy constant} * * This method never triggers initialization of this lazy constant. */ @Override int hashCode(); /** * {@return 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 {@linkplain Object#toString()} of the * content will be returned; otherwise, an implementation-dependent string is * returned that indicates this lazy constant is not yet initialized. */ @Override String toString(); // Factory /** * {@return a new lazy constant whose content is to be computed later via the * provided {@code computingFunction}} *

* The returned lazy constant strongly references the provided * {@code computingFunction} until computation completes (successfully or with * failure). *

* By design, the method always returns a new lazy constant even if the provided * computing function is already an instance of {@code LazyConstant}. Clients that * want to elide creation under this condition can write a utility method similar * to the one in the snippet below and create lazy constants via this method rather * than calling the built-in factory {@linkplain #of(Supplier)} directly: * * {@snippet lang = java: * static LazyConstant ofFlattened(Supplier computingFunction) { * return (computingFunction instanceof LazyConstant lc) * ? (LazyConstant) lc // unchecked cast is safe under normal generic usage * : LazyConstant.of(computingFunction); * } * } * * @implNote 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. * * @param computingFunction in the form of a {@linkplain Supplier} to be used * to initialize the constant * @param type of the constant * @throws NullPointerException if the provided {@code computingFunction} is * {@code null} * */ @SuppressWarnings("unchecked") static LazyConstant of(Supplier computingFunction) { Objects.requireNonNull(computingFunction); return LazyConstantImpl.ofLazy(computingFunction); } }