Which two statements about `ScopedValue` and `ThreadLocal` in Java 25 are correct? (Choose two.)
A. ScopedValue.get() throws NoSuchElementException when no binding is in effect, whereas ThreadLocal.get() returns null on a thread that never called set().Correct answer
Correct: an unbound ScopedValue read is an error — get() with no binding throws NoSuchElementException — whereas a ThreadLocal never set on this thread simply returns null (or its withInitial value); soft behaviour must be requested with isBound()/orElse().
B. A ScopedValue binding is discarded automatically when the run(...) or call(...) that established it returns, so ScopedValue has no equivalent of ThreadLocal.remove().Correct answer
Correct: a ScopedValue binding's lifetime is exactly the dynamic extent of run(...)/call(...); when that returns (normally or by throwing) the runtime tears the binding down, so there is nothing to clean up and hence no remove().
C. Because a ScopedValue lives in a static final field, the value one thread binds with where(...).run(...) is visible to every other thread until it is rebound.
Confuses the key with the binding; the static final field is the shared key, but the binding is per-thread and per-scope, so two threads binding the same ScopedValue each see only their own value and a thread with no binding sees none.
D. ScopedValue.newInstance(Supplier) supplies a default that get() returns when no binding is in effect, mirroring ThreadLocal.withInitial(Supplier).
Invents an API; ScopedValue.newInstance() takes no arguments and passing a supplier is a compile error — there is no per-ScopedValue default, only orElse/orElseThrow at the read site.
Explanation
Why `A ScopedValue binding is discarded automatically when the run(...) ...` is correct: the binding's lifetime is exactly the dynamic extent of the operation. When `run`/`call` returns — normally or by throwing — the binding is torn down by the runtime. That is why the API has no `remove()`: there is nothing left to clean up, and therefore no way to leak a value onto a long-lived (or pooled) thread.
Why `ScopedValue.get() throws NoSuchElementException when no binding ...` is correct: an unbound read is an error, not a silent null. `V.get()` with no binding throws `java.util.NoSuchElementException: ScopedValue not bound`. A plain `ThreadLocal` that was never `set` on this thread simply returns `null` (or the `withInitial` supplier's value). If you want the soft behaviour from a ScopedValue you must ask for it explicitly with `isBound()`, `orElse(...)` or `orElseThrow(...)`.
Why the others are wrong:
`ScopedValue.newInstance(Supplier) supplies a default that get() ...` invents an API. `ScopedValue.newInstance()` takes no arguments — there is no per-ScopedValue default, and passing a supplier is a compile error (`required: no arguments`). The only defaulting mechanism is `orElse`/`orElseThrow` at the read site.
`Because a ScopedValue lives in a static final field, the value one thread binds ...` confuses the *key* with the *binding*. The static final field is the key, shared by everyone; the binding is per-thread and per-scope. Two threads can run `where(V, "one")` and `where(V, "two")` at the same time and each sees only its own value, and a thread with no binding sees none at all.
Exam tip: ScopedValue trades ThreadLocal's three weaknesses — unconstrained mutability, unbounded lifetime, and expensive inheritance — for one-way, scope-bounded, immutable bindings. Expect the exam to test the consequences: no set, no remove, no default supplier, and an exception rather than null on an unbound read.