Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Java reference

ScopedValue

A value available to code only for the duration of a call — for example, the current user of a request. A replacement for ThreadLocal.

Available since: Java 25

Syntax

public final class ScopedValue<T>

Examples

A value is bound with where(...).run(...) and read with get() in any method called inside:

static final ScopedValue<String> USER = ScopedValue.newInstance();
 
void handle() {
    IO.println("Handling request for " + USER.get());
}
 
void main() {
    ScopedValue.where(USER, "Anna").run(this::handle);
    IO.println(USER.isBound());
}

Output:

Handling request for Anna
false

call() returns a result, and a nested binding temporarily replaces the value:

static final ScopedValue<String> USER = ScopedValue.newInstance();
 
String greet() {
    return "Hello, " + USER.orElse("guest");
}
 
void main() {
    IO.println(greet());
    IO.println(ScopedValue.where(USER, "Oleg").call(this::greet));
 
    ScopedValue.where(USER, "Anna").run(() -> {
        IO.println(greet());
        ScopedValue.where(USER, "Mia").run(() -> IO.println(greet()));
        IO.println(greet());
    });
}

Output:

Hello, guest
Hello, Oleg
Hello, Anna
Hello, Mia
Hello, Anna

Reading outside a binding:

static final ScopedValue<String> USER = ScopedValue.newInstance();
 
void main() {
    USER.get();
}

Output:

Exception in thread "main" java.util.NoSuchElementException: ScopedValue not bound

Notes

Unlike ThreadLocal, the value cannot be changed — only rebound for a nested call, and it disappears by itself after run() returns. This way data does not leak between requests and needs no manual cleanup. The object is usually declared as a static final field. The value is visible in child threads of StructuredTaskScope (structured concurrency, a preview in Java 25). The API became final in Java 25.

See also

In the tutorial

  • Scoped values — ScopedValue in Java 25 — passing data deep into calls without parameters, ScopedValue.newInstance, where(...).run(...), get, nested binding and why scoped values replace ThreadLocal with virtual threads.

Thanks for reading!

Donate to the site — we work hard for you.