Package org.apache.groovy.runtime.async

# Class ScopedLocal<T>

java.lang.Object
org.apache.groovy.runtime.async.ScopedLocal<T>

Type Parameters:
  `T` - the type of the scoped value

public abstract class ScopedLocal<T> extends Object
A thread-scoped value holder that abstracts over `ThreadLocal` (JDK < 25) and `java.lang.ScopedValue` (JDK 25+), presenting a unified API modelled after `ScopedValue`.

## Backend selection

On JDK 25+, where finalized `java.lang.ScopedValue` is available, this class delegates to the `ScopedValue` API. On earlier JDK versions it falls back to a conventional `ThreadLocal` with save-and-restore semantics.

## API overview

The API mirrors the main `ScopedValue` operations:

- `get()`, `orElse(Object)`, `orElseThrow(Supplier)`, `isBound()` — query the current binding.
- `where(ScopedLocal, Object)` — create a `ScopedLocal.Carrier` that can bind one or more values for a scoped execution.
- `ScopedLocal.Carrier.run(Runnable)`, `ScopedLocal.Carrier.call(Supplier)` — execute code with the bindings active.

## Usage

     private static final ScopedLocal<String> REQUEST_ID =
             ScopedLocal.newInstance();

     ScopedLocal.where(REQUEST_ID, "req-42").run(() -> {
         assert "req-42".equals(REQUEST_ID.get());
     });

     ScopedLocal.where(REQUEST_ID, "req-1")
             .where(TENANT_ID, "acme")
             .call(() -> handleRequest());

     String result = REQUEST_ID.where("req-7", () -> process());

     private static final ScopedLocal<MyCtx> CTX =
             ScopedLocal.withInitial(MyCtx::new);

Since:
  6.0.0

- ## Nested Class Summary

  Nested Classes
  Modifier and Type
  Class
  Description
  `static final class`
  `ScopedLocal.Carrier`
  An immutable set of scoped-local bindings that can be applied atomically for the duration of a `Runnable` or `Supplier`.

- ## Constructor Summary

  Constructors
  Constructor
  Description
  `ScopedLocal()`

- ## Method Summary

  Modifier and Type
  Method
  Description
  `abstract T`
  `get()`
  Returns the value bound to this scoped-local on the current thread.
  `abstract boolean`
  `isBound()`
  Returns `true` if an explicit binding is active or an initial supplier is configured.
  `static <T> ScopedLocal<T>`
  `newInstance()`
  Creates a new `ScopedLocal` with no initial value.
  `abstract T`
  `orElse(T other)`
  Returns the value bound to this scoped-local, or `other` if no binding is active and no initial supplier is configured.
  `abstract <X extends Throwable>
  T`
  `orElseThrow(Supplier<? extends X> exceptionSupplier)`
  Returns the value bound to this scoped-local if available, otherwise throws an exception produced by the `exceptionSupplier`.
  `static <T> ScopedLocal.Carrier`
  `where(ScopedLocal<T> key, T value)`
  Creates a `ScopedLocal.Carrier` that binds `key` to `value`.
  `final void`
  `where(T value, Runnable action)`
  Binds this scoped-local to `value` for the duration of the action, then restores the previous binding.
  `final <R> R`
  `where(T value, Supplier<R> supplier)`
  Binds this scoped-local to `value` for the duration of the supplier, then restores the previous binding.
  `static <T> ScopedLocal<T>`
  `withInitial(Supplier<? extends T> initialSupplier)`
  Creates a new `ScopedLocal` whose `get()` method returns a lazily initialized default when no explicit binding exists.

  ### Methods inherited from class java.lang.Object

  `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Constructor Details

  - ### ScopedLocal

    public ScopedLocal()

- ## Method Details

  - ### newInstance

    public static <T> ScopedLocal<T> newInstance()
    Creates a new `ScopedLocal` with no initial value.
    Type Parameters:
      `T` - the value type
    Returns:
      a new unbound scoped-local instance

  - ### withInitial

    public static <T> ScopedLocal<T> withInitial(Supplier<? extends T> initialSupplier)
    Creates a new `ScopedLocal` whose `get()` method returns a lazily initialized default when no explicit binding exists. The supplier is invoked at most once per thread and the result is cached, analogous to `ThreadLocal.withInitial(Supplier)`.
    Type Parameters:
      `T` - the value type
    Parameters:
      `initialSupplier` - supplies the default value
    Returns:
      a new scoped-local instance with a default supplier

  - ### where

    public static <T> ScopedLocal.Carrier where(ScopedLocal<T> key, T value)
    Creates a `ScopedLocal.Carrier` that binds `key` to `value`. The binding takes effect when `ScopedLocal.Carrier.run(Runnable)` or `ScopedLocal.Carrier.call(Supplier)` is invoked.
    Type Parameters:
      `T` - the value type
    Parameters:
      `key` - the scoped-local to bind
      `value` - the value to bind; may be `null`
    Returns:
      a carrier holding the binding

  - ### get

    public abstract T get()
    Returns the value bound to this scoped-local on the current thread.
    Returns:
      the current value
    Throws:
      `NoSuchElementException` - if no value is bound and no initial supplier was provided

  - ### orElse

    public abstract T orElse(T other)
    Returns the value bound to this scoped-local, or `other` if no binding is active and no initial supplier is configured.
    Parameters:
      `other` - the fallback value
    Returns:
      the current value or `other`

  - ### orElseThrow

    public abstract <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
    Returns the value bound to this scoped-local if available, otherwise throws an exception produced by the `exceptionSupplier`.

    If an initial supplier is configured (via `withInitial(Supplier)`), this method returns the initial value rather than throwing.

    Type Parameters:
      `X` - the type of the exception to throw if not bound
    Parameters:
      `exceptionSupplier` - a supplier that produces the exception to throw; must not be `null`
    Returns:
      the current value
    Throws:
      `X` - if no value is bound and no initial supplier is configured
      `NullPointerException` - if `exceptionSupplier` is `null`

  - ### isBound

    public abstract boolean isBound()
    Returns `true` if an explicit binding is active or an initial supplier is configured.
    Returns:
      whether a value is available via `get()`

  - ### where

    public final <R> R where(T value, Supplier<R> supplier)
    Binds this scoped-local to `value` for the duration of the supplier, then restores the previous binding.
    Type Parameters:
      `R` - the result type
    Parameters:
      `value` - the value to bind; may be `null`
      `supplier` - the action to execute with the binding active
    Returns:
      the supplier's result

  - ### where

    public final void where(T value, Runnable action)
    Binds this scoped-local to `value` for the duration of the action, then restores the previous binding.
    Parameters:
      `value` - the value to bind; may be `null`
      `action` - the action to execute with the binding active