Module java.base
Package java.lang

Interface ComputedConstant<V>

Type Parameters:
V - The type of the value to be bound
All Superinterfaces:
Supplier<V>

public sealed interface ComputedConstant<V> extends Supplier<V>
ComputedConstant is a preview API of the Java platform.
Programs can only use ComputedConstant when preview features are enabled.
Preview features may be removed in a future release, or upgraded to permanent features of the Java platform.
An immutable value holder that is initialized at most once via a provider, offering the performance and safety benefits of final fields, while providing greater flexibility as to the timing of initialization.

A computed constant can be queried after being created to provide a bound value, for example when get() is invoked.

Once bound (or if it failed to bound), computed constant instances are guaranteed to be lock free and are eligible for constant folding optimizations by the JVM. Providers of constant values are guaranteed to be invoked at most one time. In other words, a provider can only run once and in the first-calling thread and so, there is no race across threads which guarantees the at-most-once evaluation. The life cycle of a computed constant is said to be monotonic where it can go from the initial state of unbound (when it is not associated with any value) eventually to the terminal state of bound (when it is permanently associated with a fixed value). The value can be null.

This contrasts with AtomicReference where any number of updates can be done and where there is no simple way to atomically compute a value (guaranteed to only be computed once) if missing. Computed constants also contrasts to Future where a value is computed in another thread.

The implementations are optimized for providing high average performance for get operations over many invocations.

Computed Constant Factories

New instances of ComputedConstants can only be obtained via the following factory methods:

ComputedConstant

ComputedConstant provides atomic evaluation using a provider:
     class DemoPreset {

         private static final ComputedConstant<Foo> FOO = ComputedConstant.of(Foo::new); // provider = Foo::new

         public Foo theFoo() {
             // Foo is lazily constructed and recorded here upon first invocation
             return FOO.get();
         }
     }
The performance of the get() method in the example above is on par with using an inner/private class holding a lazily initialized variable but with no overhead imposed by the extra holder class. Such a holder class might implement a lazy value as follows:
     class DemoHolder {

         public Foo theBar() {
             class Holder {
                 private static final Foo FOO = new Foo();
             }

             // Foo is lazily constructed and recorded here upon first invocation
             return Holder.FOO;
         }
     }
Here is how a constant value can be computed in the background so that it may already be bound when first requested from user code:
     class DemoBackground {

         private static final ComputedConstant<Foo> CONSTANT = ComputedConstant.of(Foo::new);

         static {
             Thread.ofVirtual().start(CONSTANT::get);
         }

         public static void main(String[] args) throws InterruptedException {
             Thread.sleep(1000);
             // CONSTANT is likely already pre-computed here by a background thread
             System.out.println(CONSTANT.get());
         }
     }
ComputedConstant<T> implements Supplier<T> allowing simple interoperability with legacy code and less specific type declaration as shown in the following example:
     class SupplierDemo {

         // Eager Supplier of Foo
         private static final Supplier<Foo> EAGER_FOO = Foo::new;

         // Turns an eager Supplier into a caching constant Supplier
         private static final Supplier<Foo> LAZILY_CACHED_FOO = ComputedConstant.of(EAGER_FOO);

         public static void main(String[] args) {
             // Lazily compute the one-and-only `Foo`
             Foo theFoo = LAZILY_CACHED_FOO.get();
         }
     }

List of ComputedConstant Elements

Lists of ComputedConstant elements can also be obtained via ComputedConstant factory methods in a similar way as for ComputedConstant instances but with an extra initial arity, indicating the desired size of the List:
     class DemoArray {

         // 1. Declare a list of ComputedConstant elements of size 32
         private static final List<ComputedConstant<Long>> VALUE_PO2_CACHE =
             ComputedConstant.of(32, index -> 1L << index); // mappingProvider = index -> 1L << index

         public long powerOfTwo(int n) {
             // 2. The n:th slot is computed and bound here upon the
             //    first call of get(n). The other elements are not affected.
             // 3. Using an n outside the list will throw an IndexOutOfBoundsException
             return VALUE_PO2_CACHE.get(n).get();
         }
     }
As can be seen above, a List factory takes an IntFunction rather than a Supplier, allowing custom values to be computed and bound for elements in the list depending on the current index being used.

States

ComputedConstant instances maintain an internal state described as follows:
  • Unbound
    • Indicates no value is bound (transient state)
    • Can move to "Binding"
    • This state can be detected using the method isUnbound()
  • Binding
    • Indicates an attempt to bind a value is in progress (transient state)
    • Can move to "Bound" or "Error"
    • This state can be unreliably assumed using a combination of the "is" predicates (where all are false)
  • Bound
    • Indicates a value is successfully bound (final state)
    • Cannot move
    • This state can be detected using the method isBound()
  • Error
    • Indicates an error when trying to bind a value (final state)
    • Cannot move
    • This state can be detected using the method isError()
Transient states can change at any time, whereas if a final state is observed, it is guaranteed the state will never change again.

The internal states and their transitions are depicted below where gray nodes indicate final states:

the internal states

General Properties of ComputedConstant

All methods of this class will throw a NullPointerException if a reference parameter is null unless otherwise specified. All computed constant constructs are "null-friendly" meaning a value can be bound to null. As usual, values of type Optional may also express optionality, without using null, as exemplified here:
     class NullDemo {

         private Supplier<Optional<Color>> backgroundColor =
                 ComputedConstant.of(() -> Optional.ofNullable(calculateBgColor()));

         Color backgroundColor(Color defaultColor) {
             return backgroundColor.get()
                     .orElse(defaultColor);
         }

         private Color calculateBgColor() {
             // Read background color from file returning "null" if it fails.
             // ...
             return null;
         }
     }
Since:
22
  • Method Summary

    Modifier and Type
    Method
    Description
    get()
    Returns the bound value of this computed constant. If no value is bound, atomically attempts to compute and record a bound value using the pre-set provider.
    boolean
    Returns true if a value is bound to this constant.
    boolean
    Returns true if an attempt was made to bind a value but a value could not be bound to this constant.
    boolean
    Returns true if no attempt has been made to bind a value to this constant.
    default <R> ComputedConstantPREVIEW<R>
    map(Function<? super V,? extends R> mapper)
    Returns a new ComputedConstantPREVIEW that will use this computed constant's eventually bound value and then apply the provided mapper as the pre-set provider.
    of(int size, IntFunction<? extends V> mappingProvider)
    Returns a new unmodifiable List of ComputedConstantPREVIEW elements with the provided size and given pre-set mappingProvider to be used to compute element values.
    static <V, T extends V>
    ComputedConstantPREVIEW<V>
    of(Class<T> superType, MethodHandle provider)
    Returns a new ComputedConstantPREVIEW with the given pre-set provider to be used to compute a value and provided superType indicating the most specific return type that can be expressed.
    of(Supplier<? extends V> provider)
    Returns a new ComputedConstantPREVIEW with the given pre-set provider to be used to compute a value.
    orElse(V other)
    Returns the bound value of this computed constant. If no value is bound, atomically attempts to compute and record a bound value using the pre-set provider or, if the provider throws an unchecked exception, returns the provided other value.
    <X extends Throwable>
    V
    orElseThrow(Supplier<? extends X> exceptionSupplier)
    Returns the bound value of this computed constant. If no value is bound, atomically attempts to compute and record a bound value using the pre-set provider or, if the provider throws an unchecked exception, throws an exception produced by invoking the provided exceptionSupplier function.
  • Method Details

    • isUnbound

      boolean isUnbound()
      Returns true if no attempt has been made to bind a value to this constant.
      Returns:
      true if no attempt has been made to bind a value to this constant
    • isBound

      boolean isBound()
      Returns true if a value is bound to this constant.
      Returns:
      true if a value is bound to this constant
    • isError

      boolean isError()
      Returns true if an attempt was made to bind a value but a value could not be bound to this constant.
      Returns:
      true if an attempt was made to bind a value but a value could not be bound to this constant
    • get

      V get()
      Returns the bound value of this computed constant. If no value is bound, atomically attempts to compute and record a bound value using the pre-set provider.

      If the provider returns null, null is bound and returned. If the provider throws an (unchecked) exception, the exception is wrapped into a NoSuchElementException which is thrown, and no value is bound. If an Error is thrown by the provider, the Error is relayed to the caller. If an Exception or an Error is thrown by the provider, no further attempt is made to bind the value and all subsequent invocations of this method will throw a new NoSuchElementException.

      The most common usage is to construct a new object serving as a memoized result, as in:

          ComputedConstant<V> constant = ComputedConstant.of(Value::new);
          // ...
          V value = constant.get();
          assertNotNull(value); // Value is non-null
      

      If a thread calls this method while being bound by another thread, the current thread will be suspended until the binding completes (successfully or not). Otherwise, this method is guaranteed to be lock-free.

      Specified by:
      get in interface Supplier<V>
      Returns:
      the bound value of this computed constant. If no value is bound, atomically attempts to compute and record a bound value using the pre-set provider
      Throws:
      NoSuchElementException - if a value cannot be bound
      StackOverflowError - if a circular dependency is detected (i.e. the provider calls itself directly or indirectly in the same thread).
      Error - if the provider throws an Error
    • orElse

      V orElse(V other)
      Returns the bound value of this computed constant. If no value is bound, atomically attempts to compute and record a bound value using the pre-set provider or, if the provider throws an unchecked exception, returns the provided other value.

      If a thread calls this method while being bound by another thread, the current thread will be suspended until the binding completes (successfully or not). Otherwise, this method is guaranteed to be lock-free.

      Parameters:
      other - to use if no value neither is bound nor can be bound (can be null)
      Returns:
      the bound value of this computed constant. If no value is bound, atomically attempts to compute and record a bound value using the pre-set provider or, if the provider throws an unchecked exception, returns the provided other value
      Throws:
      NoSuchElementException - if a value cannot be bound
      StackOverflowError - if a circular dependency is detected (i.e. the provider calls itself directly or indirectly in the same thread).
      Error - if the provider throws an Error
    • orElseThrow

      <X extends Throwable> V orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
      Returns the bound value of this computed constant. If no value is bound, atomically attempts to compute and record a bound value using the pre-set provider or, if the provider throws an unchecked exception, throws an exception produced by invoking the provided exceptionSupplier function.

      If a thread calls this method while being bound by another thread, the current thread will be suspended until the binding completes (successfully or not). Otherwise, this method is guaranteed to be lock-free.

      Type Parameters:
      X - the type of the exception that may be thrown
      Parameters:
      exceptionSupplier - the supplying function that produces the exception to throw
      Returns:
      the bound value of this computed constant. If no value is bound, atomically attempts to compute and record a bound value using the pre-set provider or, if the provider throws an unchecked exception, throws an exception produced by invoking the provided exceptionSupplier function
      Throws:
      X - if a value cannot be bound.
      Error - if the provider throws an Error
    • map

      default <R> ComputedConstantPREVIEW<R> map(Function<? super V,? extends R> mapper)
      Returns a new ComputedConstantPREVIEW that will use this computed constant's eventually bound value and then apply the provided mapper as the pre-set provider.
      Type Parameters:
      R - the return type of the provided mapper
      Parameters:
      mapper - to apply to this computed constant
      Returns:
      a new ComputedConstantPREVIEW that will use this computed constant's eventually bound value and then apply the provided mapper as the pre-set provider
    • of

      static <V> ComputedConstantPREVIEW<V> of(Supplier<? extends V> provider)
      Returns a new ComputedConstantPREVIEW with the given pre-set provider to be used to compute a value.

      If a later attempt is made to invoke any of the get(), orElse(Object) or orElseThrow(Supplier) methods when this computed constant is unbound, the provider will automatically be invoked.

           class DemoPreset {
      
               private static final ComputedConstant<Foo> FOO = ComputedConstant.of(Foo::new);
      
               public Foo theBar() {
                   // Foo is lazily constructed and recorded here upon first invocation
                   return FOO.get();
               }
           }
      
      Type Parameters:
      V - the type of the value
      Parameters:
      provider - to invoke when computing a value
      Returns:
      a new ComputedConstantPREVIEW with the given pre-set provider to be used to compute a value
    • of

      static <V, T extends V> ComputedConstantPREVIEW<V> of(Class<T> superType, MethodHandle provider)
      Returns a new ComputedConstantPREVIEW with the given pre-set provider to be used to compute a value and provided superType indicating the most specific return type that can be expressed.

      If a later attempt is made to invoke any of the get(), orElse(Object) or orElseThrow(Supplier) methods when this computed constant is unbound, the provider will automatically be invoked.

           class DemoPreset {
      
               private static final ComputedConstant<Foo> FOO = ComputedConstant.of(createFooMH());
      
               public Foo theBar() {
                   // Foo is lazily constructed and recorded here upon first invocation
                   return FOO.get();
               }
           }
      
      Type Parameters:
      V - the type of the value
      T - a superclass of the value
      Parameters:
      superType - a class indicating the most specific return type (that can be expressed) of the provider (which is used for type inference and method handle validation)
      provider - to invoke when computing a value
      Returns:
      a new ComputedConstantPREVIEW with the given pre-set provider to be used to compute a value and provided superType indicating the most specific return type that can be expressed
      Throws:
      IllegalArgumentException - if the given MethodHandle provider has a call signature return type that is not assignable from the provided superType or if it takes any parameters.
    • of

      static <V> List<ComputedConstantPREVIEW<V>> of(int size, IntFunction<? extends V> mappingProvider)
      Returns a new unmodifiable List of ComputedConstantPREVIEW elements with the provided size and given pre-set mappingProvider to be used to compute element values.

      The List and its elements are eligible for constant folding optimizations by the JVM.

      Below, an example of how to cache values in a list is shown:

           class DemoList {
      
               private static final List<ComputedConstant<Long>> PO2_CACHE =
                       ComputedConstant.of(32, index -> 1L << index);
      
               public long powerOfTwoValue(int n) {
                   return PO2_CACHE.get(n);
               }
           }
      
      API Note:
      The list is free to return value-based ComputedConstant elements that has no valid identity.
      Type Parameters:
      V - the type of the values
      Parameters:
      size - the size of the List
      mappingProvider - to invoke when computing and binding element values
      Returns:
      a new unmodifiable List of ComputedConstantPREVIEW elements with the provided size and given pre-set mappingProvider to be used to compute element values