자바 언어·플랫폼 용어 사전
모던 자바LazyConstant · StableValue · Stable Values

Lazy Constants

스레드 안전한 지연 초기화를 언어가 대신 해 준다. 25의 StableValue가 26에서 이름·API가 바뀌었다.

한 번만 계산해 두고 계속 쓰는 값을 안전하게 만드는 기능. 홀더 관용구와 이중 검사 잠금을 대체한다.

// Java 26 (JEP 526)
private final LazyConstant<Config> cfg = LazyConstant.of(this::loadConfig);
Config cfg() { return cfg.get(); }        // 최초 1회만 loadConfig 가 돈다

무엇을 대체하나

지금까지 스레드 안전한 지연 초기화는 손으로 짜야 했다.

  • 홀더 관용구 — 정적 중첩 클래스를 만들어 클래스 로딩의 락에 얹는다. 안전하지만 인스턴스 필드에는 못 쓴다
  • 이중 검사 잠금volatile 을 빠뜨리면 조용히 깨진다. 가장 유명한 동시성 버그의 무대

LazyConstant 는 둘의 제약을 다 없앤다. 인스턴스 필드로 쓸 수 있고, 동기화를 직접 다루지 않으며, final 이라 JIT 이 초기화 이후를 상수로 취급할 수 있다.

이름과 API 가 바뀌었다

Java 25 (JEP 502)Java 26 (JEP 526)
타입StableValueLazyConstant
만들기StableValue.of() — 빈 상자LazyConstant.of(계산함수)
읽기orElseSet(계산함수)get()
저수준orElseSet·setOrThrow·trySet전부 제거
null허용불허

설계 방향이 "빈 상자를 만들고 나중에 채운다" 에서 "계산 방법을 미리 주고 알아서 채우게 한다" 로 옮겼다. 값이 없는 중간 상태가 사라져 잘못 쓸 여지가 준다. null 을 막은 것도 같은 맥락이다 — 읽기 경로에서 분기 하나가 없어진다.

지연 컬렉션

List<Double> roots = List.ofLazy(100, Math::sqrt);          // 꺼낼 때 계산
Map<Locale, ResourceBundle> b = Map.ofLazy(locales, this::loadBundle);

원소 100개를 미리 다 계산하지 않고 실제로 꺼내는 것만 계산한다. 로케일별 리소스 번들처럼 가짓수는 많은데 실제로 쓰는 건 몇 개 인 경우에 맞는다.

프리뷰라는 점을 잊지 않는다

25 에서 26 으로 오며 타입 이름과 주요 메서드가 통째로 바뀌었고, isInitialized()·orElse(T) 는 Java 27 에서 제거될 예정이다. 프리뷰 API 를 운영 코드에 굳히면 다음 버전에서 컴파일이 깨진다. 정식화될 때까지는 홀더 관용구가 여전히 안전한 선택지다.

면접 함정

  • "Stable Values 라는 기능이 있다" → 25 의 이름이다. 26 에서 Lazy Constants 로 바뀌었다.
  • "지연 초기화는 volatile + 이중 검사가 정석" → 그것은 제약이 많은 옛 방법이다. 홀더 관용구가 먼저고, 앞으로는 이 기능이 대체한다.
  • "프리뷰 기능도 켜서 쓰면 된다" → API 가 릴리스마다 바뀐다. LTS 정식화 전에는 코드에 굳히지 않는다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 모듈·모던 Java