✅ Переменные с областью действия
Ещё одно интересное нововведение, которое ждёт нас в грядущем релизе JDK 25 — переменные с областью действия.
Наверняка многие сталкивались с ситуацией, когда нужно передать какую-то информацию между двумя компонентами, но при этом по каким-то причинам не хочется добавлять новые аргументы в методы или свойства в классы. Зачастую такая задача решалась при помощи ThreadLocal - локальных переменных потока. В одном месте данные добавлялись в локальные переменные потока, а в другом - извлекались. Но у локальных переменных потоков есть свои недостатки и достаточно существенные. И именно недостатки ThreadLocal побудили разработчиков JDK придумать новый инструмент, позволяющий объявлять переменные в некоторой области действия.
Переменные с областью действия находились в разработке с JDK 20 и к релизу JDK 25 стали стабильными. Переменные с областью действия описываются типизированным классом ScopedValue<T>, где тип соответствует типу данных переменной. Данный класс предоставляет следующие методы:
newInstance()— метод класса для создания переменной с областью действияwhere()— метод класса для присвоения значения и получения его носителяget()— для получения значения переменной для текущей области действияisBound()— для проверки наличия присвоенного значенияorElse()— для получения текущего значения или альтернативногоorElseThorw()— для получения текущего значения или выбрасывания исключения
Носитель значения описывается классом ScopedValue.Carrier и предоставляет следующие методы:
get()— для получения значения для текущей области действияwhere()— для присвоения нового значения и получения дочернего носителя
Пример использования переменных с областью действия:
// Создание новой строковой переменной с областью действия
ScopedValue<String> scopedValue = ScopedValue.newInstance();
// Присваивание значения и получение носителя
ScopedValue.Carrier carrier = ScopedValue.where(scopedValue, "foo");
// Выполнение кода с передачей переменной
carrier.run(() -> {
// Здесь значение переменной - foo
// Выведет foo
IO.println(scopedValue.get());
// Присваивание значения и получение нового носителя
ScopedValue.where(scopedValue, "bar").run(() -> {
// А здесь значение переменной - bar
// Выведет bar
IO.println(scopedValue.get());
});
// В этой области действия значение осталось прежним - foo
// Выведет foo
IO.println(scopedValue.get());
});
// К текущему значению можно получить доступ через носитель
// Выведет foo
IO.println(carrier.get());
// Так делать нельзя, так как для текущей области действия
// значение не назначено, и будет выброшено
// исключение NoSuchElementException scopedValue.get();
