Interface StableValue<T>

Type Parameters:
T - type of the holder value

public sealed interface StableValue<T>
StableValue is a preview API of the Java platform.
Programs can only use StableValue when preview features are enabled.
Preview features may be removed in a future release, or upgraded to permanent features of the Java platform.
A thin, atomic, thread-safe, set-at-most-once, stable value holder eligible for certain JVM optimizations if set to a value.

A stable value is said to be monotonic because the state of a stable value can only go from unset to set and consequently, a value can only be set at most once.

StableValue is mainly intended to be a member of a holding class and is usually neither exposed directly via accessors nor passed as a method parameter.

Factories

To create a new fresh (unset) StableValue, use the StableValue::newInstance factory.

This class also contains a number of convenience methods for creating constructs involving stable values:

  • A caching (also called "memoized") Supplier, where a given original Supplier is guaranteed to be successfully invoked at most once even in a multithreaded environment, can be created like this:
        Supplier<T> cache = StableValue.newCachingSupplier(original);
    
  • A caching (also called "memoized") IntFunction, for the allowed given size input values [0, size) and where the given original IntFunction is guaranteed to be successfully invoked at most once per inout index even in a multithreaded environment, can be created like this:
         IntFunction<R> cache = StableValue.newCachingIntFunction(size, original);
    
  • A caching (also called "memoized") Function, for the given set of allowed inputs and where the given original function is guaranteed to be successfully invoked at most once per input value even in a multithreaded environment, can be created like this:
       Function<T, R> cache = StableValue.newCachingFunction(inputs, original);
    
  • A lazy List of stable elements with a given size and given mapper can be created the following way:
        List<E> lazyList = StableValue.lazyList(size, mapper);
    
    The list can be used to model stable one-dimensional arrays. If two- or more dimensional arrays are to be modeled, a List of List of ... of E can be used.
  • A lazy Map with a given set of keys and given mapper associated with stable values can be created like this:
        Map<K, V> lazyMap = StableValue.lazyMap(keys, mapper);
    

The constructs above are eligible for similar JVM optimizations as StableValue instances.

Memory Consistency Properties

Actions on a presumptive holder value in a thread prior to calling a method that sets the holder value are seen by any other thread that observes a set holder value. More generally, the action of attempting to interact (i.e. via load or store operations) with a StableValue's holder value (e.g. via trySet(T) or orElseThrow()) forms a happens-before relation between any other attempt to interact with the StableValue's holder value.

Nullability

Except for a StableValue's holder value itself, all method parameters must be non-null or a NullPointerException will be thrown.
Implementation Note:
Implementing classes are free to synchronize on this and consequently, care should be taken whenever (directly or indirectly) synchronizing on a StableValue. Failure to do this may lead to deadlock., Instance fields explicitly declared as StableValue or one-dimensional arrays thereof are eligible for certain JVM optimizations compared to normal instance fields. 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.
Since:
24
  • Method Summary

    Modifier and Type
    Method
    Description
    computeIfUnset(Supplier<? extends T> supplier)
    Returns the set holder value if set, otherwise attempts to compute and set a new (nullable) value using the provided supplier, returning the (pre-existing or newly set) value.
    boolean
    Returns true if a holder value is set, false otherwise.
    static <T> List<T>
    lazyList(int size, IntFunction<? extends T> mapper)
    Returns a lazy, shallowly immutable, stable List of the provided size where the individual elements of the list are lazily computed via the provided mapper whenever an element is first accessed (directly or indirectly), for example via List::get.
    static <K, V> Map<K,V>
    lazyMap(Set<K> keys, Function<? super K,? extends V> mapper)
    Returns a lazy, shallowly immutable, stable Map of the provided keys where the associated values of the maps are lazily computed vio the provided mapper whenever a value is first accessed (directly or indirectly), for example via Map::get.
    static <T, R> Function<T,R>
    newCachingFunction(Set<? extends T> inputs, Function<? super T,? extends R> original)
    Returns a new caching, thread-safe, stable, lazily computed Function that, for each allowed input in the given set of inputs, records the values of the provided original Function upon being first accessed via Function::apply, or optionally via background threads created from the provided factory (if non-null).
    static <R> IntFunction<R>
    newCachingIntFunction(int size, IntFunction<? extends R> original)
    Returns a new caching, thread-safe, stable, lazily computed IntFunction that, for each allowed input, records the values of the provided original IntFunction upon being first accessed via IntFunction::apply.
    static <T> Supplier<T>
    newCachingSupplier(Supplier<? extends T> original)
    Returns a new caching, thread-safe, stable, lazily computed supplier that records the value of the provided original supplier upon being first accessed via Supplier::get.
    static <T> StableValuePREVIEW<T>
    Returns a fresh stable value with an unset holder value.
    orElse(T other)
    Returns the set holder value (nullable) if set, otherwise return the other value.
    Returns the set holder value if set, otherwise throws NoSuchElementException.
    default void
    setOrThrow(T value)
    Sets the holder value to the provided value, or, if already set, throws IllegalStateException}
    boolean
    trySet(T value)
    Returns true if the holder value was set to the provided value, otherwise returns false.
  • Method Details

    • trySet

      boolean trySet(T value)
      Returns true if the holder value was set to the provided value, otherwise returns false.

      When this method returns, a holder value is always set.

      Parameters:
      value - to set (nullable)
      Returns:
      true if the holder value was set to the provided value, otherwise returns false
    • orElse

      T orElse(T other)
      Returns the set holder value (nullable) if set, otherwise return the other value.
      Parameters:
      other - to return if the stable holder value is not set
      Returns:
      the set holder value (nullable) if set, otherwise return the other value
    • orElseThrow

      T orElseThrow()
      Returns the set holder value if set, otherwise throws NoSuchElementException.
      Returns:
      the set holder value if set, otherwise throws NoSuchElementException
      Throws:
      NoSuchElementException - if no value is set
    • isSet

      boolean isSet()
      Returns true if a holder value is set, false otherwise.
      Returns:
      true if a holder value is set, false otherwise
    • setOrThrow

      default void setOrThrow(T value)
      Sets the holder value to the provided value, or, if already set, throws IllegalStateException}

      When this method returns (or throws an Exception), a holder value is always set.

      Parameters:
      value - to set (nullable)
      Throws:
      IllegalStateException - if a holder value is already set
    • computeIfUnset

      T computeIfUnset(Supplier<? extends T> supplier)
      Returns the set holder value if set, otherwise attempts to compute and set a new (nullable) value using the provided supplier, returning the (pre-existing or newly set) value.

      The provided supplier is guaranteed to be invoked at most once if it completes without throwing an exception.

      If the supplier throws an (unchecked) exception, the exception is rethrown, and no value is set. The most common usage is to construct a new object serving as a lazily computed value or memoized result, as in:

       
       Value witness = stable.computeIfUnset(Value::new);
       
      Implementation Requirements:
      The implementation logic is equivalent to the following steps for this stable:
       
       if (stable.isSet()) {
           return stable.get();
       } else {
           V newValue = supplier.get();
           stable.setOrThrow(newValue);
           return newValue;
       }
       
      Except it is thread-safe and will only return the same witness value regardless if invoked by several threads. Also, the provided supplier will only be invoked once even if invoked from several threads unless the supplier throws an exception.
      Parameters:
      supplier - to be used for computing a value
      Returns:
      the set holder value if set, otherwise attempts to compute and set a new (nullable) value using the provided supplier, returning the (pre-existing or newly set) value
      Throws:
      StackOverflowError - if the provided supplier recursively invokes the provided supplier upon being invoked.
    • newInstance

      static <T> StableValuePREVIEW<T> newInstance()
      Returns a fresh stable value with an unset holder value.
      Type Parameters:
      T - type of the holder value
      Returns:
      a fresh stable value with an unset holder value
    • newCachingSupplier

      static <T> Supplier<T> newCachingSupplier(Supplier<? extends T> original)
      Returns a new caching, thread-safe, stable, lazily computed supplier that records the value of the provided original supplier upon being first accessed via Supplier::get.

      The provided original supplier is guaranteed to be successfully invoked at most once even in a multi-threaded environment. Competing threads invoking the Supplier::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.

      If the original Supplier invokes the returned Supplier recursively, a StackOverflowError will be thrown when the returned Supplier's Supplier::get method is invoked.

      If the provided original supplier throws an exception, it is relayed to the initial caller.

      Type Parameters:
      T - the type of results supplied by the returned supplier
      Parameters:
      original - supplier used to compute a memoized value
      Returns:
      a new caching, thread-safe, stable, lazily computed supplier that records the value of the provided original supplier upon being first accessed via Supplier::get
    • newCachingIntFunction

      static <R> IntFunction<R> newCachingIntFunction(int size, IntFunction<? extends R> original)
      Returns a new caching, thread-safe, stable, lazily computed IntFunction that, for each allowed input, records the values of the provided original IntFunction upon being first accessed via IntFunction::apply.

      The provided original IntFunction is guaranteed to be successfully invoked at most once per allowed input, even in a multi-threaded environment. Competing threads invoking the IntFunction::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.

      If the original IntFunction invokes the returned IntFunction recursively for a particular input value, a StackOverflowError will be thrown when the returned IntFunction's IntFunction::apply method is invoked.

      If the provided original IntFunction throws an exception, it is relayed to the initial caller.

      Type Parameters:
      R - the type of results delivered by the returned IntFunction
      Parameters:
      size - the size of the allowed inputs in [0, size)
      original - IntFunction used to compute a memoized value
      Returns:
      a new caching, thread-safe, stable, lazily computed IntFunction that, for each allowed input, records the values of the provided original IntFunction upon being first accessed via IntFunction::apply
    • newCachingFunction

      static <T, R> Function<T,R> newCachingFunction(Set<? extends T> inputs, Function<? super T,? extends R> original)
      Returns a new caching, thread-safe, stable, lazily computed Function that, for each allowed input in the given set of inputs, records the values of the provided original Function upon being first accessed via Function::apply, or optionally via background threads created from the provided factory (if non-null).

      The provided original Function is guaranteed to be successfully invoked at most once per allowed input, even in a multi-threaded environment. Competing threads invoking the Function::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.

      If the original Function invokes the returned Function recursively for a particular input value, a StackOverflowError will be thrown when the returned Function's Function::apply method is invoked.

      If the provided original Function throws an exception, it is relayed to the initial caller. If the memoized Function is computed by a background thread, exceptions from the provided original Function will be relayed to the background thread's uncaught exception handler.

      The order in which background threads are started is unspecified.

      Type Parameters:
      T - the type of the input to the returned Function
      R - the type of results delivered by the returned Function
      Parameters:
      inputs - the set of allowed input values
      original - Function used to compute a memoized value
      Returns:
      a new caching, thread-safe, stable, lazily computed Function that, for each allowed input in the given set of inputs, records the values of the provided original Function upon being first accessed via Function::apply, or optionally via background threads created from the provided factory (if non-null)
    • lazyList

      static <T> List<T> lazyList(int size, IntFunction<? extends T> mapper)
      Returns a lazy, shallowly immutable, stable List of the provided size where the individual elements of the list are lazily computed via the provided mapper whenever an element is first accessed (directly or indirectly), for example via List::get.

      The provided mapper IntFunction 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.

      If the mapper IntFunction invokes the returned IntFunction recursively for a particular index, a StackOverflowError will be thrown when the returned List's List::get method is invoked.

      If the provided mapper IntFunction throws an exception, it is relayed to the initial caller and no element is computed.

      The returned List is not Serializable

      Type Parameters:
      T - the StableValues' element type
      Parameters:
      size - the size of the returned list
      mapper - to invoke whenever an element is first accessed (may return null)
      Returns:
      a lazy, shallowly immutable, stable List of the provided size where the individual elements of the list are lazily computed via the provided mapper whenever an element is first accessed (directly or indirectly), for example via List::get
    • lazyMap

      static <K, V> Map<K,V> lazyMap(Set<K> keys, Function<? super K,? extends V> mapper)
      Returns a lazy, shallowly immutable, stable Map of the provided keys where the associated values of the maps are lazily computed vio the provided mapper whenever a value is first accessed (directly or indirectly), for example via Map::get.

      The provided mapper Function is guaranteed to be successfully invoked at most once per key, even in a multi-threaded environment. Competing threads accessing an associated value already under computation will block until an associated value is computed or an exception is thrown by the computing thread.

      If the mapper Function invokes the returned Map recursively for a particular key, a StackOverflowError will be thrown when the returned Map's Map::get} method is invoked.

      If the provided mapper Function throws an exception, it is relayed to the initial caller and no value is computed.

      The returned Map is not Serializable

      Type Parameters:
      K - the type of keys maintained by the returned map
      V - the type of mapped values
      Parameters:
      keys - the keys in the returned map
      mapper - to invoke whenever an associated value is first accessed (may return null)
      Returns:
      a lazy, shallowly immutable, stable Map of the provided keys where the associated values of the maps are lazily computed vio the provided mapper whenever a value is first accessed (directly or indirectly), for example via Map::get