diff --git a/src/java.base/share/classes/java/lang/StableValue.java b/src/java.base/share/classes/java/lang/StableValue.java
new file mode 100644
index 0000000000000..dbbe696c7b019
--- /dev/null
+++ b/src/java.base/share/classes/java/lang/StableValue.java
@@ -0,0 +1,620 @@
+/*
+ * Copyright (c) 2024, 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.stable.StableValueImpl;
+import jdk.internal.lang.stable.StableValueFactories;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.RandomAccess;
+import java.util.Set;
+import java.util.function.Function;
+import java.util.function.IntFunction;
+import java.util.function.Supplier;
+
+/**
+ * A stable value is a holder of deferred immutable data.
+ * <p>
+ * A {@linkplain StableValue {@code StableValue<T>}} is created using the factory method
+ * {@linkplain StableValue#unset() {@code StableValue.unset()}}. When created, the
+ * stable value is <em>unset</em>, which means it holds no value. Its holder value, of
+ * type {@code T}, can be <em>set</em> by calling
+ * {@linkplain #trySet(Object) trySet()}, {@linkplain #setOrThrow(Object) setOrThrow()},
+ * or {@linkplain #computeIfUnset(Supplier) computeIfUnset()}. Once set, the holder value
+ * can never change and can be retrieved by calling
+ * {@linkplain #orElseThrow() orElseThrow()}, {@linkplain #orElse(Object) orElse()}, or
+ * {@linkplain #computeIfUnset(Supplier) computeIfUnset()}.
+ * <p>
+ * A stable value that is <em>set</em> is treated as a constant by the JVM, enabling the
+ * same performance optimizations that are available for {@code final} fields.
+ * As such, stable values can be used to replace {@code final} fields in cases where
+ * <em>at-most-once</em> update semantics is crucial, but where the eager initialization
+ * semantics associated with {@code final} fields is too restrictive.
+ * <p>
+ * Consider the following example where a stable value field "{@code logger}" is an
+ * immutable holder of a value of type {@code Logger} and that is initially created
+ * as <em>unset</em>, which means it holds no value. Later in the example, the
+ * state of the "{@code logger}" field is checked and if it is still <em>unset</em>,
+ * a holder value is <em>set</em>:
+ *
+ * {@snippet lang = java:
+ * class Component {
+ *
+ *    // Creates a new unset stable value with no holder value
+ *    // @link substring="unset" target="#unset" :
+ *    private final StableValue<Logger> logger = StableValue.unset();
+ *
+ *    Logger getLogger() {
+ *        if (!logger.isSet()) {
+ *            logger.trySet(Logger.create(Component.class));
+ *        }
+ *         return logger.orThrow();
+ *    }
+ *
+ *    void process() {
+ *        logger.get().info("Process started");
+ *        // ...
+ *    }
+ * }
+ *}
+ * <p>
+ * Note that the holder value can only be set at most once.
+ * <p>
+ * To guarantee that, even under races, only one instance of {@code Logger} is ever
+ * created, the {@linkplain #computeIfUnset(Supplier) computeIfUnset()} method can be used
+ * instead, where the holder is atomically and lazily computed via a lambda expression:
+ *
+ * {@snippet lang = java:
+ * class Component {
+ *
+ *    // Creates a new unset stable value with no holder value
+ *    // @link substring="unset" target="#unset" :
+ *    private final StableValue<Logger> logger = StableValue.unset();
+ *
+ *    Logger getLogger() {
+ *        return logger.computeIfUnset( () -> Logger.create(Component.class) );
+ *    }
+ *
+ *    void process() {
+ *        logger.get().info("Process started");
+ *        // ...
+ *    }
+ * }
+ *}
+ * <p>
+ * The {@code getLogger()} method calls {@code logger.computeIfUnset()} on the
+ * stable value to retrieve its holder value. If the stable value is <em>unset</em>, then
+ * {@code computeIfUnset()} evaluates and sets the holder value; the holder value is then
+ * returned to the client. In other words, {@code computeIfUnset()} guarantees that a
+ * stable value's holder value is <em>set</em> before it is used.
+ * <p>
+ * Furthermore, {@code computeIfUnset()} guarantees that the lambda expression provided is
+ * evaluated only once, even when {@code logger.computeIfUnset()} is invoked concurrently.
+ * This property is crucial as evaluation of the lambda expression may have side effects,
+ * e.g., the call above to {@code Logger.getLogger()} may result in storage resources
+ * being prepared.
+ *
+ * <h2 id="stable-functions">Stable Functions</h2>
+ * Stable values provide the foundation for higher-level functional abstractions. A
+ * <em>stable supplier</em> is a supplier that computes a value and then caches it into
+ * a backing stable value storage for later use. A stable supplier is created via the
+ * {@linkplain StableValue#ofSupplier(Supplier) StableValue.ofSupplier()} factory,
+ * by providing an original {@linkplain Supplier} which is invoked when the
+ * stable supplier is first accessed:
+ *
+ * {@snippet lang = java:
+ *     class Component {
+ *
+ *         private final Supplier<Logger> logger =
+ *                 // @link substring="ofSupplier" target="#ofSupplier(Supplier)" :
+ *                 StableValue.ofSupplier( () -> Logger.getLogger(Component.class) );
+ *
+ *         void process() {
+ *            logger.get().info("Process started");
+ *            // ...
+ *         }
+ *     }
+ *}
+ * A stable supplier encapsulates access to its backing stable value storage. This means
+ * that code inside {@code Component} can obtain the logger object directly from the
+ * stable supplier, without having to go through an accessor method like {@code getLogger()}.
+ * <p>
+ * A <em>stable int function</em> is a function that takes an {@code int} parameter and
+ * uses it to compute a result that is then cached into the backing stable value storage
+ * for that parameter value. A stable int function is created via the
+ * {@linkplain StableValue#ofIntFunction(int, IntFunction) StableValue.ofIntFunction()}
+ * factory. Upon creation, the input range (i.e. [0, size)) is specified together with
+ * an original {@linkplain IntFunction} which is invoked at most once per input value. In
+ * effect, the stable int function will act like a cache for the original {@linkplain IntFunction}:
+ *
+ * {@snippet lang = java:
+ *     class SqrtUtil {
+ *
+ *         private static final IntFunction<Double> SQRT =
+ *                 // @link substring="ofIntFunction" target="#ofIntFunction(int,IntFunction)" :
+ *                 StableValue.ofIntFunction(10, StrictMath::sqrt);
+ *
+ *         double sqrt9() {
+ *             return SQRT.apply(9); // Eventually constant folds to 3.0 at runtime
+ *         }
+ *
+ *     }
+ *}
+ * <p>
+ * A <em>stable function</em> is a function that takes a parameter (of type {@code T}) and
+ * uses it to compute a result that is then cached into the backing stable value storage
+ * for that parameter value. A stable function is created via the
+ * {@linkplain StableValue#ofFunction(Set, Function) StableValue.ofFunction()} factory.
+ * Upon creation, the input {@linkplain Set} is specified together with an original {@linkplain Function}
+ * which is invoked at most once per input value. In effect, the stable function will act
+ * like a cache for the original {@linkplain Function}:
+ *
+ * {@snippet lang = java:
+ *     class SqrtUtil {
+ *
+ *         private static final Function<Integer, Double> SQRT =
+ *                 // @link substring="ofFunction" target="#ofFunction(Set,Function)" :
+ *                 StableValue.ofFunction(Set.of(1, 2, 4, 8, 16, 32), StrictMath::sqrt);
+ *
+ *         double sqrt16() {
+ *             return SQRT.apply(16); // Eventually constant folds to 4.0 at runtime
+ *         }
+ *
+ *     }
+ *}
+ *
+ * <h2 id="stable-collections">Stable Collections</h2>
+ * Stable values can also be used as backing storage for
+ * {@linkplain Collection##unmodifiable unmodifiable collections}. A <em>stable list</em>
+ * is an unmodifiable list, backed by an array of stable values. The stable list elements
+ * are computed when they are first accessed, using the provided {@linkplain IntFunction}:
+ *
+ * {@snippet lang = java:
+ *     class SqrtUtil {
+ *
+ *         private static final List<Double> SQRT =
+ *                 // @link substring="ofList" target="#ofList(int,IntFunction)" :
+ *                 StableValue.ofList(10, StrictMath::sqrt);
+ *
+ *         double sqrt9() {
+ *             return SQRT.apply(9); // Eventually constant folds to 3.0 at runtime
+ *         }
+ *
+ *     }
+ *}
+ * <p>
+ * Note: In the example above, there is a constructor in the {@code Component}
+ *       class that takes an {@code int} parameter.
+ * <p>
+ * Similarly, a <em>stable map</em> is an unmodifiable map whose keys are known at
+ * construction. The stable map values are computed when they are first accessed,
+ * using the provided {@linkplain Function}:
+ *
+ * {@snippet lang = java:
+ *     class SqrtUtil {
+ *
+ *         private static final Map<Integer, Double> SQRT =
+ *                 // @link substring="ofMap" target="#ofMap(Set,Function)" :
+ *                 StableValue.ofMap(Set.of(1, 2, 4, 8, 16, 32), StrictMath::sqrt);
+ *
+ *         double sqrt16() {
+ *             return SQRT.apply(16); // Eventually constant folds to 4.0 at runtime
+ *         }
+ *
+ *     }
+ *}
+ *
+ * <h2 id="composition">Composing stable values</h2>
+ * A stable value can depend on other stable values, thereby creating a dependency graph
+ * that can be lazily computed but where access to individual elements still can be
+ * constant-folded. 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 stable values:
+ * {@snippet lang = java:
+ *     class Dependency {
+ *
+ *         public static class Foo {
+ *              // ...
+ *          }
+ *
+ *         public static class Bar {
+ *             public Bar(Foo foo) {
+ *                  // ...
+ *             }
+ *         }
+ *
+ *         private static final Supplier<Foo> FOO = StableValue.ofSupplier(Foo::new);
+ *         private static final Supplier<Bar> BAR = StableValue.ofSupplier(() ->  new Bar(FOO.get()));
+ *
+ *         public static Foo foo() {
+ *             return FOO.get();
+ *         }
+ *
+ *         public static Bar bar() {
+ *             return BAR.get();
+ *         }
+ *
+ *     }
+ *}
+ * Calling {@code bar()} will create the {@code Bar} singleton if it is not already
+ * created. Upon such a creation, the dependent {@code Foo} will first be created if
+ * the {@code Foo} does not already exist.
+ * <p>
+ * Here is another example where a more complex dependency graph is created in which
+ * integers in the Fibonacci delta series are lazily computed:
+ * {@snippet lang = java:
+ *     class Fibonacci {
+ *
+ *         private static final int MAX_SIZE_INT = 46;
+ *
+ *         private static final IntFunction<Integer> FIB =
+ *                 StableValue.ofIntFunction(MAX_SIZE_INT, Fibonacci::fib);
+ *
+ *         public static int fib(int n) {
+ *             return n < 2
+ *                     ? n
+ *                     : FIB.apply(n - 1) + FIB.apply(n - 2);
+ *         }
+ *
+ *     }
+ * }
+ * Both {@code FIB} and {@code Fibonacci::fib} recurses into each other. Because the
+ * stable int function {@code FIB} caches intermediate results, the initial
+ * computational complexity is reduced from exponential to linear compared to a
+ * traditional non-caching recursive fibonacci method. Once computed, the VM can
+ * constant-fold expressions like {@code Fibonacci.fib(10)}.
+ * <p>
+ * The fibonacci example above is a dependency graph with no circular dependencies (i.e.,
+ * it is a dependency tree). If there are circular dependencies in a dependency graph,
+ * a stable value will eventually throw a {@linkplain StackOverflowError} upon referencing
+ * elements in a circularity.
+ *
+ * <h2 id="thread-safety">Thread Safety</h2>
+ * A holder value is guaranteed to be set at most once. If competing threads are
+ * racing to set a stable value, only one update succeeds, while other updates are
+ * blocked until the stable value becomes set.
+ * <p>
+ * The at-most-once write operation on a stable value (e.g. {@linkplain #trySet(Object) trySet()})
+ * <a href="{@docRoot}/java.base/java/util/concurrent/package-summary.html#MemoryVisibility"><i>happens-before</i></a>
+ * any subsequent read operation (e.g. {@linkplain #orElseThrow()}).
+ * <p>
+ * The method {@linkplain StableValue#computeIfUnset(Supplier) computeIfUnset()}
+ * guarantees that the provided {@linkplain Supplier} is invoked successfully at most
+ * once even under race. Since stable functions and stable collections are built on top
+ * of {@linkplain StableValue#computeIfUnset(Supplier) computeIfUnset()} they too are
+ * thread safe and guarantee at-most-once-per-input invocation.
+ *
+ * <h2 id="miscellaneous">Miscellaneous</h2>
+ * Except for a StableValue's holder value itself, all method parameters must be
+ * <em>non-null</em> or a {@link NullPointerException} will be thrown.
+ * <p>
+ * Stable functions and collections are not {@link Serializable} as this would require
+ * {@linkplain #ofList(int, IntFunction) mappers} to be {@link Serializable} as well,
+ * which would introduce security vulnerabilities.
+ * <p>
+ * As objects can be set via stable values but never removed, this can be a source
+ * of unintended memory leaks. A stable value's set values are
+ * {@linkplain java.lang.ref##reachability strongly reachable}. Clients are advised that
+ * {@linkplain java.lang.ref##reachability reachable} stable values will hold set values
+ * perpetually.
+ *
+ * @implSpec Implementing classes of {@linkplain StableValue} are free to synchronize on
+ *           {@code this} and consequently, care should be taken whenever
+ *           (directly or indirectly) synchronizing on a {@code StableValue}. Failure to
+ *           do this may lead to deadlock. Stable functions and collections on the
+ *           other hand are guaranteed <em>not to synchronize</em> on {@code this}.
+ *
+ * @implNote A {@linkplain StableValue} is mainly intended to be a non-public field in
+ *           a class and is usually neither exposed directly via accessors nor passed as
+ *           a method parameter.
+ *           Instance fields explicitly declared as {@code StableValue} or one-dimensional
+ *           arrays thereof are eligible for certain JVM optimizations where normal
+ *           instance fields are not. This comes with restrictions on reflective
+ *           modifications. Although most ways of reflective modification of such fields
+ *           are disabled, it is strongly discouraged to circumvent these protection means
+ *           as reflectively modifying such fields may lead to unspecified behavior.
+ *
+ * @param <T> type of the holder value
+ *
+ * @since 25
+ */
+@PreviewFeature(feature = PreviewFeature.Feature.STABLE_VALUES)
+public sealed interface StableValue<T>
+        permits StableValueImpl {
+
+    // Principal methods
+
+    /**
+     * {@return {@code true} if the holder value was set to the provided
+     *          {@code value}, {@code false} otherwise}
+     * <p>
+     * When this method returns, the holder value is always set.
+     *
+     * @param value to set
+     */
+    boolean trySet(T value);
+
+    /**
+     * {@return the holder value if set, otherwise, returns the provided
+     *          {@code other} value}
+     *
+     *
+     * @param other to return if the holder value is not set
+     */
+    T orElse(T other);
+
+    /**
+     * {@return the holder value if set, otherwise, throws {@code NoSuchElementException}}
+     *
+     * @throws NoSuchElementException if no holder value is set
+     */
+    T orElseThrow();
+
+    /**
+     * {@return {@code true} if the holder value is set, {@code false} otherwise}
+     */
+    boolean isSet();
+
+    /**
+     * {@return the holder value; if unset, first attempts to compute and set the
+     *          holder value using the provided {@code supplier}}
+     * <p>
+     * The provided {@code supplier} is guaranteed to be invoked at most once if it
+     * completes without throwing an exception.
+     * <p>
+     * If the supplier throws an (unchecked) exception, the exception is rethrown, and no
+     * holder value is set. The most common usage is to construct a new object serving
+     * as a lazily computed value or memoized result, as in:
+     *
+     * <pre> {@code
+     * Value witness = stable.computeIfUnset(Value::new);
+     * }</pre>
+     * <p>
+     * When this method returns successfully, the holder value is always set.
+     *
+     * @implSpec The implementation logic is equivalent to the following steps for this
+     *           {@code stable}:
+     *
+     * <pre> {@code
+     * if (stable.isSet()) {
+     *     return stable.get();
+     * } else {
+     *     V newValue = supplier.get();
+     *     stable.setOrThrow(newValue);
+     *     return newValue;
+     * }
+     * }</pre>
+     * Except it is thread-safe and will only return the same witness value
+     * regardless if invoked by several threads. Also, the provided {@code supplier}
+     * will only be invoked once even if invoked from several threads unless the
+     * {@code supplier} throws an exception.
+     *
+     * @param  supplier to be used for computing the holder value
+     */
+    T computeIfUnset(Supplier<? extends T> supplier);
+
+    // Convenience methods
+
+    /**
+     * Sets the holder value to the provided {@code value}, or,
+     * if already set, throws {@code IllegalStateException}.
+     * <p>
+     * When this method returns (or throws an exception), the holder value is always set.
+     *
+     * @param value to set
+     * @throws IllegalStateException if the holder value was already set
+     */
+    void setOrThrow(T value);
+
+    /**
+     * {@return {@code true} if {@code this == obj}, {@code false} otherwise}
+     *
+     * @param obj to check for equality
+     */
+    boolean equals(Object obj);
+
+    /**
+     * {@return the {@linkplain System#identityHashCode(Object) identity hash code} of
+     *          {@code this} object}
+     */
+    int hashCode();
+
+    // Factories
+
+    /**
+     * {@return a new unset stable value}
+     * <p>
+     * An unset stable value has no holder value.
+     *
+     * @param <T> type of the holder value
+     */
+    static <T> StableValue<T> unset() {
+        return StableValueFactories.unset();
+    }
+
+    /**
+     * {@return a new set stable value holding the provided {@code value}}
+     *
+     * @param value holder value to set
+     * @param <T>   type of the holder value
+     */
+    static <T> StableValue<T> of(T value) {
+        return StableValueFactories.of(value);
+    }
+
+    /**
+     * {@return a new unset stable supplier}
+     * <p>
+     * The returned {@linkplain Supplier supplier} is a caching supplier that records
+     * the value of the provided {@code original} supplier upon being first accessed via
+     * the returned supplier's {@linkplain Supplier#get() get()} method.
+     * <p>
+     * The provided {@code original} supplier is guaranteed to be successfully invoked
+     * at most once even in a multi-threaded environment. Competing threads invoking the
+     * returned supplier's {@linkplain Supplier#get() get()} method when a value is
+     * already under computation will block until a value is computed or an exception is
+     * thrown by the computing thread.
+     * <p>
+     * If the provided {@code original} supplier throws an exception, it is relayed
+     * to the initial caller and no holder value is recorded.
+     *
+     * @param original supplier used to compute a cached value
+     * @param <T>      the type of results supplied by the returned supplier
+     */
+    static <T> Supplier<T> ofSupplier(Supplier<? extends T> original) {
+        Objects.requireNonNull(original);
+        return StableValueFactories.ofSupplier(original);
+    }
+
+    /**
+     * {@return a new unset stable int function}
+     * <p>
+     * The returned {@link IntFunction int function} is a caching int function that,
+     * for each allowed input, records the values of the provided {@code original}
+     * int function upon being first accessed via the returned int function's
+     * {@linkplain IntFunction#apply(int) apply()} method.
+     * <p>
+     * The provided {@code original} int function is guaranteed to be successfully invoked
+     * at most once per allowed input, even in a multi-threaded environment. Competing
+     * threads invoking the returned int function's
+     * {@linkplain IntFunction#apply(int) apply()} method when a value is already under
+     * computation will block until a value is computed or an exception is thrown by
+     * the computing thread.
+     * <p>
+     * If the provided {@code original} int function throws an exception, it is relayed
+     * to the initial caller and no holder value is recorded.
+     *
+     * @param size     the size of the allowed inputs in {@code [0, size)}
+     * @param original IntFunction used to compute cached values
+     * @param <R>      the type of results delivered by the returned IntFunction
+     */
+    static <R> IntFunction<R> ofIntFunction(int size,
+                                            IntFunction<? extends R> original) {
+        if (size < 0) {
+            throw new IllegalArgumentException();
+        }
+        Objects.requireNonNull(original);
+        return StableValueFactories.ofIntFunction(size, original);
+    }
+
+    /**
+     * {@return a new unset stable function}
+     * <p>
+     * The returned {@link Function function} is a caching function that, for each allowed
+     * input in the given set of {@code inputs}, records the values of the provided
+     * {@code original} function upon being first accessed via the returned function's
+     * {@linkplain Function#apply(Object) apply()} method.
+     * <p>
+     * The provided {@code original} function is guaranteed to be successfully invoked
+     * at most once per allowed input, even in a multi-threaded environment. Competing
+     * threads invoking the returned function's {@linkplain Function#apply(Object) apply()}
+     * method when a value is already under computation will block until a value is
+     * computed or an exception is thrown by the computing thread.
+     * <p>
+     * If the provided {@code original} function throws an exception, it is relayed to
+     * the initial caller and no holder value is recorded.
+     *
+     * @param inputs   the set of allowed input values
+     * @param original Function used to compute cached values
+     * @param <T>      the type of the input to the returned Function
+     * @param <R>      the type of results delivered by the returned Function
+     */
+    static <T, R> Function<T, R> ofFunction(Set<? extends T> inputs,
+                                            Function<? super T, ? extends R> original) {
+        Objects.requireNonNull(inputs);
+        Objects.requireNonNull(original);
+        return StableValueFactories.ofFunction(inputs, original);
+    }
+
+    /**
+     * {@return a new unset stable list with the provided {@code size}}
+     * <p>
+     * The returned list is an {@linkplain Collection##unmodifiable unmodifiable} list
+     * whose size is known at construction. The list's elements are computed via the
+     * provided {@code mapper} when they are first accessed
+     * (e.g. via {@linkplain List#get(int) List::get}).
+     * <p>
+     * The provided {@code mapper} int function is guaranteed to be successfully invoked
+     * at most once per list index, even in a multi-threaded environment. Competing
+     * threads accessing an element already under computation will block until an element
+     * is computed or an exception is thrown by the computing thread.
+     * <p>
+     * If the provided {@code mapper} throws an exception, it is relayed to the initial
+     * caller and no value for the element is recorded.
+     * <p>
+     * The returned list and its {@link List#subList(int, int) subList} views implement
+     * the {@link RandomAccess} interface.
+     *
+     * @param size   the size of the returned list
+     * @param mapper to invoke whenever an element is first accessed
+     *               (may return {@code null})
+     * @param <E>    the type of elements in the returned list
+     */
+    static <E> List<E> ofList(int size,
+                              IntFunction<? extends E> mapper) {
+        if (size < 0) {
+            throw new IllegalArgumentException();
+        }
+        Objects.requireNonNull(mapper);
+        return StableValueFactories.ofList(size, mapper);
+    }
+
+    /**
+     * {@return a new unset stable map with the provided {@code keys}}
+     * <p>
+     * The returned map is an {@linkplain Collection##unmodifiable unmodifiable} map whose
+     * keys are known at construction. The map's values are computed via the provided
+     * {@code mapper} when they are first accessed
+     * (e.g. via {@linkplain Map#get(Object) Map::get}).
+     * <p>
+     * The provided {@code mapper} function is guaranteed to be successfully invoked
+     * at most once per key, even in a multi-threaded environment. Competing
+     * threads accessing a value already under computation will block until an element
+     * is computed or an exception is thrown by the computing thread.
+     * <p>
+     * If the provided {@code mapper} throws an exception, it is relayed to the initial
+     * caller and no value associated with the provided key is recorded.
+     *
+     * @param keys   the keys in the returned map
+     * @param mapper to invoke whenever an associated value is first accessed
+     *               (may return {@code null})
+     * @param <K>    the type of keys maintained by the returned map
+     * @param <V>    the type of mapped values in the returned map
+     */
+    static <K, V> Map<K, V> ofMap(Set<K> keys,
+                                  Function<? super K, ? extends V> mapper) {
+        Objects.requireNonNull(keys);
+        Objects.requireNonNull(mapper);
+        return StableValueFactories.ofMap(keys, mapper);
+    }
+
+}