-
Notifications
You must be signed in to change notification settings - Fork 6.1k
8366178: Implement JEP 526: Lazy Constants (Second Preview) #27605
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 105 commits
f0b4055
4ec5c95
7d4c788
b60832c
fc4f00b
c37cd69
c93a3fe
9c52870
307c592
bbf2385
9705d7c
05d702f
a3dfa92
5c2cdd7
ba51c34
b547ae4
5122d70
8356bed
a79c648
7420756
fcf6d15
d617a22
e244ecc
43b6b2e
74aa729
bf6a92e
d1a90e2
592b1cb
fd821d9
29d7b9b
ffc98d7
5c3c25a
eed78e1
27ec2f2
872f9d8
a24b3eb
6613a78
c5258b8
b6a9b65
dd63ae9
65f07c5
83ad77f
2af02a6
2343b93
8ed5f30
80beb92
acee335
33b65fa
6c0e72c
96d0dd3
ab95ccd
c8b4432
b95caec
2d7ecdf
8bd1808
e1f2ef3
761fbe9
cf85f05
0c1df0e
5614441
ef58ec2
e67ba2c
b2983e3
e41270d
0fcc78c
24e1e7d
72fc01d
4abb43a
0540866
8472562
8338a37
e3496a3
b361d9d
ee7a7be
d33cc04
dc77f6a
9b742cb
a2b2dc7
f402701
43e6ef4
a902970
1f72ef9
36f4f0a
9331a5f
e45b1ed
3e28703
0978179
5770e03
47af430
ea14b8f
e25eeb9
5845a7a
8b9c683
8fa4a4c
e6268f3
f549390
a51f221
6a6e804
8cde697
77ae3fe
e2c24f5
d961426
698b412
fc064e4
6721ca4
c3278e0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,303 @@ | ||
| /* | ||
| * Copyright (c) 2025, 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.*; | ||
| import java.util.function.Function; | ||
| import java.util.function.IntFunction; | ||
| import java.util.function.Supplier; | ||
|
|
||
| /** | ||
| * A lazy constant is a holder of contents that can be set at most once. | ||
| * <p> | ||
| * A lazy constant is created using the factory method | ||
| * {@linkplain LazyConstant#of(Supplier) LazyConstant.of({@code <computing function>})}. | ||
| * When created, the lazy constant is <em>not initialized</em>, meaning it has no contents. | ||
| * The lazy constant (of type {@code T}) can then be <em>initialized</em> | ||
| * (and its contents retrieved) by calling {@linkplain #get()}. The first time | ||
| * {@linkplain #get()} is called, the underlying <em>computing function</em> | ||
| * (provided at construction) will be invoked and the result will be used to initialize | ||
| * the constant. Once a lazy constant is initialized, its contents can <em>never change</em> | ||
| * and will be retrieved over and over again upon subsequent {@linkplain #get() get} | ||
| * invocations. | ||
| * <p> | ||
| * 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> logger = | ||
| * // @link substring="of" target="#of" : | ||
| * LazyConstant.of( () -> Logger.create(Component.class) ); | ||
| * | ||
| * public void process() { | ||
| * logger.get().info("Process started"); | ||
| * // ... | ||
| * } | ||
| * } | ||
| *} | ||
| * <p> | ||
| * Initially, the lazy constant is <em>not initialized</em>. 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()} | ||
| * guarantees that the constant is <em>initialized</em> before it returns, barring | ||
| * any exceptions. | ||
| * <p> | ||
| * Furthermore, {@linkplain #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. | ||
| * | ||
| * <h2 id="exception-handling">Exception handling</h2> | ||
| * If the computing function returns {@code null}, a {@linkplain NullPointerException} | ||
| * is 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. | ||
| * <p> | ||
| * If the computing function recursively invokes itself (directly or indirectly via | ||
| * the lazy constant), an {@linkplain IllegalStateException} is thrown, and the lazy | ||
| * constant is not initialized. | ||
| * <p> | ||
| * If the computing function throws any unchecked {@linkplain Exception} or | ||
| * {@linkplain Error}, that {@linkplain Throwable} is propagated to the caller, and the | ||
| * lazy constant remains uninitialized. | ||
| * | ||
| * <h2 id="composition">Composing lazy constants</h2> | ||
| * 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 final class DependencyUtil { | ||
| * | ||
| * private DependencyUtil() {} | ||
| * | ||
| * public static class Foo { | ||
| * // ... | ||
| * } | ||
| * | ||
| * public static class Bar { | ||
| * public Bar(Foo foo) { | ||
| * // ... | ||
| * } | ||
| * } | ||
| * | ||
| * private static final LazyConstant<Foo> FOO = LazyConstant.of( Foo::new ); | ||
| * private 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(); | ||
| * } | ||
| * | ||
| * } | ||
| *} | ||
| * 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. | ||
| * | ||
| * <h2 id="thread-safety">Thread Safety</h2> | ||
| * A lazy constant is guaranteed to be initialized atomically and at most once. If | ||
minborg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| * 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 | ||
| * <em>the computing thread</em>), while the other threads are blocked until the constant | ||
| * is initialized, after which the other threads observe the lazy constant is initialized | ||
| * and leave the constant unchanged and will never invoke any computation. | ||
| * <p> | ||
| * The invocation of the computing function and the resulting initialization of | ||
| * the constant {@linkplain java.util.concurrent##MemoryVisibility <em>happens-before</em>} | ||
minborg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| * 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. | ||
| * <p> | ||
| * 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}. | ||
| * <p> | ||
| * If the computing function blocks indefinitely, other threads operating on this | ||
| * lazy constant may block indefinitely; no timeouts or cancellations are provided. | ||
| * | ||
| * <h2 id="performance">Performance</h2> | ||
| * The contents 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 contents and instead use the contents | ||
| * that has been previously observed. We call this optimization <em>constant folding</em>. | ||
| * 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 <em>trusted fields</em> (i.e., {@code static final} fields, | ||
| * {@linkplain Record record} fields, lazy constants, lazy lists, lazy maps, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lazy constants and lazy lists/maps are not themselves "trusted final fields" (at least not yet), so not sure they belong here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There could be a chain: |
||
| * or final instance fields in hidden classes) -- to a lazy constant. | ||
| * | ||
| * <h2 id="miscellaneous">Miscellaneous</h2> | ||
| * Except for {@linkplain Object#equals(Object) equals(obj)} and | ||
| * {@linkplain #orElse(Object) orElse(other)} parameters, all method parameters | ||
| * must be <em>non-null</em>, or a {@link NullPointerException} will be thrown. | ||
| * | ||
| * @apiNote Once a lazy constant is initialized, its contents cannot ever be removed. | ||
| * This can be a source of an unintended memory leak. More specifically, | ||
| * a lazy constant {@linkplain java.lang.ref##reachability strongly references} | ||
| * it contents. Hence, a lazy constant will hold its contents until | ||
| * the lazy constant itself is collected (if ever). | ||
| * <p> | ||
| * 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. | ||
| * <p> | ||
| * The {@code LazyConstant} type is not {@link Serializable}. | ||
| * <p> | ||
| * Use in static initializers may interact with class initialization order; | ||
| * cyclic initialization may result in initialization errors as described | ||
| * in {@jls 12.4} of <cite>The Java Language Specification</cite>. | ||
| * | ||
| * @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 contents may or may not be | ||
| * immutable (e.g., it may hold an {@linkplain ArrayList}). | ||
| * | ||
| * @param <T> 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<T> | ||
| extends Supplier<T> | ||
| permits LazyConstantImpl { | ||
|
|
||
| /** | ||
| * {@return the contents of this lazy constant if initialized, otherwise,\ | ||
| * returns {@code other}} | ||
| * <p> | ||
| * This method never triggers initialization of this lazy constant and will observe | ||
| * initialization by other threads atomically (i.e., it returns the contents | ||
| * if and only if the initialization has already completed). | ||
| * | ||
| * @param other value to return if the content is not initialized | ||
| * (can be {@code null}) | ||
| */ | ||
| T orElse(T other); | ||
|
|
||
| /** | ||
| * {@return the contents of this initialized constant. If not initialized, first | ||
| * computes and initializes this constant using the computing function} | ||
| * <p> | ||
| * After this method returns successfully, the constant is guaranteed to be | ||
| * initialized. | ||
| * <p> | ||
| * If the computing function throws, the throwable is relayed to the caller and | ||
| * the lazy constant remains uninitialized; a subsequent call to get() may then | ||
| * attempt the computation again. | ||
| */ | ||
| T get(); | ||
|
|
||
| /** | ||
| * {@return {@code true} if the constant is initialized, {@code false} otherwise} | ||
| * <p> | ||
| * This method never triggers initialization of this lazy constant and will observe | ||
| * changes in the initialization state made by other threads atomically. | ||
| */ | ||
| boolean isInitialized(); | ||
|
|
||
| // Object methods | ||
|
|
||
| /** | ||
| * {@return if this lazy constant is the same as the provided {@code obj}} | ||
| * | ||
| * This method never triggers initialization of this lazy constant. | ||
| */ | ||
| @Override | ||
| boolean equals(Object obj); | ||
minborg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * {@return the {@linkplain System#identityHashCode(Object)} for this lazy constant} | ||
| * | ||
| * This method never triggers initialization of this lazy constant. | ||
| */ | ||
| @Override | ||
| int hashCode(); | ||
|
|
||
| /** | ||
| * {@return a non-initializing string suitable for debugging} | ||
| * <p> | ||
| * This method never triggers initialization of this lazy constant and will observe | ||
| * initialization by other threads atomically (i.e., it observes the | ||
| * contents if and only if the initialization has already completed). | ||
| * <p> | ||
| * If this lazy constant is initialized, the {@linkplain Object#toString()} of the | ||
| * contents will be returned; otherwise, an implementation-dependent string is | ||
| * returned that indicates this lazy constant is not yet initialized. | ||
| */ | ||
| @Override | ||
| String toString(); | ||
minborg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| // Factory | ||
|
|
||
| /** | ||
| * {@return a lazy constant to be computed later via the provided | ||
| * {@code computingFunction}} | ||
| * <p> | ||
| * The returned lazy constant strongly references the provided | ||
| * {@code computingFunction} until initialization completes successfully; after | ||
| * which the computing function is no longer strongly referenced and becomes | ||
| * eligible for garbage collection. | ||
| * <p> | ||
| * If the provided computing function is already an instance of | ||
| * {@code LazyConstant}, the method is free to return the provided computing function | ||
| * directly. | ||
| * | ||
| * @param computingFunction in the form of a {@linkplain Supplier} to be used | ||
| * to initialize the constant | ||
| * @param <T> type of the constant | ||
| * | ||
| */ | ||
| @SuppressWarnings("unchecked") | ||
| static <T> LazyConstant<T> of(Supplier<? extends T> computingFunction) { | ||
| Objects.requireNonNull(computingFunction); | ||
| if (computingFunction instanceof LazyConstant<? extends T> lc) { | ||
| return (LazyConstant<T>) lc; | ||
| } | ||
| return LazyConstantImpl.ofLazy(computingFunction); | ||
| } | ||
|
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you wanted, you could reuse the
Componentclass -- that one already has a lazy constant field, so if you put the component itself in another lazy constant field you have your composition example.