선택 심화: 프리뷰 소스 격리
필수 입문 과정을 마친 학습자가 Java 25 프리뷰 기능의 플래그, 소스 세트와 산출물 격리를 선택적으로 실험합니다.
이 장은 선택 심화 과정입니다. Java 문법, 객체지향, 컬렉션, 예외, 파일, 네트워크와 JVM 실행 원리를 먼저 익힌 뒤 관심이 있을 때 진행하세요. 이후 핵심 장은 이 장의 프리뷰 기능을 안다고 가정하지 않으므로 건너뛰어도 자습 흐름에 문제가 없습니다.
프리뷰 기능은 완성된 Java SE 규칙이 아니라 개발자가 피드백을 제공하도록 공개한 제한적 기능입니다. Java 25의 Stable Values, 구조화된 동시성, 기본형 패턴은 프리뷰이며 기본적으로 비활성입니다. 소스와 클래스 파일 모두 Java 25에 종속되고 다음 릴리스에서 API나 문법이 바뀌거나 사라질 수 있습니다. 따라서 안정적인 애플리케이션 소스와 같은 빌드 경로에 섞지 않습니다.
프리뷰 플래그 없는 StableValue 참조
아래 소스를 기본 javac로 컴파일하면 StableValue is a preview API and is disabled by default 계열 오류가 발생합니다.
클래스가 JDK에 존재한다는 사실만으로 정식 API라고 판단할 수 없습니다.
public final class PreviewFeatureWithoutFlagFailure {
public static void main(String[] args) {
StableValue<String> value = StableValue.of();
System.out.println(value.orElseSet(() -> "preview"));
}
}실행한 명령과 관찰 결과는 다음과 같습니다.
javac --release 25 PreviewFeatureWithoutFlagFailure.java
# error: StableValue is a preview API and is disabled by default원칙은 프리뷰 소스에만 컴파일과 실행 플래그를 함께 적용하는 것입니다. 컴파일만 플래그를 붙이면 프리뷰 클래스 파일을 실행 환경이 거부할 수 있습니다.
Stable Values 실행 경로
StableValue는 Java 25의 JEP 502 프리뷰 API입니다.
지연 초기화 블록을 여러 번 호출해도 값은 최대 한 번 설정됩니다.
아래 예제는 반드시 프리뷰 플래그로 컴파일·실행합니다.
import java.util.concurrent.atomic.AtomicInteger;
public final class StableValuePreviewProbe {
public static void main(String[] args) {
StableValue<String> value = StableValue.of();
AtomicInteger calls = new AtomicInteger();
String first = value.orElseSet(() -> "value-" + calls.incrementAndGet());
String second = value.orElseSet(() -> "value-" + calls.incrementAndGet());
System.out.println("first=" + first);
System.out.println("second=" + second);
System.out.println("initializer-calls=" + calls.get());
}
}javac --enable-preview --release 25 -Xlint:preview StableValuePreviewProbe.java
java --enable-preview StableValuePreviewProbe
# first=value-1
# second=value-1
# initializer-calls=1-Xlint:preview 경고를 숨기지 않고 산출물 로그에 보존합니다.
프리뷰 사용 위치가 늘어나면 검토에서 즉시 보이게 합니다.
기본형 패턴의 프리뷰 플래그
Java 25 JEP 507은 기본형 타입 패턴과 long·float·double·boolean switch를 세 번째 프리뷰로 제공합니다.
다음 소스는 기본 컴파일에서 거부되고 프리뷰 컴파일에서 실행됩니다.
public final class PrimitivePatternPreviewProbe {
static String classify(long value) {
return switch (value) {
case 0L -> "zero";
case long number when number > 0 -> "positive:" + number;
case long number -> "negative:" + number;
};
}
public static void main(String[] args) {
System.out.println(classify(0L));
System.out.println(classify(25L));
System.out.println(classify(-7L));
}
}javac --enable-preview --release 25 -Xlint:preview PrimitivePatternPreviewProbe.java
java --enable-preview PrimitivePatternPreviewProbe
# zero
# positive:25
# negative:-7--source 25보다 --release 25를 사용해 언어와 플랫폼 API 대상을 함께 고정합니다.
소스 실행기를 쓸 때는 java --enable-preview File.java 형태를 사용하지만 CI 산출물 검사는 컴파일과 실행 단계를 분리하는 편이 명확합니다.
소스 세트와 산출물 디렉터리
안정 소스는 src/main/java, 프리뷰 실험은 src/preview/java처럼 분리하고 산출물도 build/classes/main, build/classes/preview로 나눕니다.
안정 산출물 패키징 작업이 프리뷰 산출물을 참조하지 않게 의존성 그래프를 확인합니다.
import java.nio.file.Path;
import java.util.List;
public final class PreviewPathPolicy {
private record Compilation(Path source, Path output, boolean preview) {}
static List<String> javacArguments(Compilation compilation) {
java.util.ArrayList<String> arguments = new java.util.ArrayList<>();
if (compilation.preview()) {
arguments.add("--enable-preview");
arguments.add("--release");
arguments.add("25");
arguments.add("-Xlint:preview");
} else {
arguments.add("--release");
arguments.add("25");
}
arguments.add("-d");
arguments.add(compilation.output().toString());
arguments.add(compilation.source().toString());
return List.copyOf(arguments);
}
public static void main(String[] args) {
Compilation stable =
new Compilation(
Path.of("src/main/java/App.java"), Path.of("build/classes/main"), false);
Compilation preview =
new Compilation(
Path.of("src/preview/java/Lab.java"),
Path.of("build/classes/preview"),
true);
System.out.println(javacArguments(stable));
System.out.println(javacArguments(preview));
}
}프리뷰 실험 선택 기준
- 운영 요구 사항이 아니라 학습·평가 목표로 사용합니다.
- Stable Values에서는 지연 초기화 API의 의미와 경합을 관찰합니다.
- 구조화된 동시성에서는 작업 수명과 실패·취소 모델을 실험합니다.
- 기본형 패턴은 정확한 변환과 분기 가독성을 평가합니다.
- 기능별 JEP 버전과 다음 JDK에서의 변경 사항을 기록합니다.
- 안정 산출물의 의존성과 실행 플래그에 프리뷰가 전파되지 않게 합니다.
공식 기준은 JDK 25 기능 목록과 각 JEP입니다. 오래된 블로그의 생성자나 메서드 시그니처를 복사하지 않고 현재 JDK 25 API로 컴파일합니다.
JDK 25가 LTS라면 그 안의 프리뷰도 LTS 안정성을 갖나요?
아닙니다. LTS는 공급자의 지원 정책이고 프리뷰 기능의 명세 상태를 바꾸지 않습니다. JDK 25가 장기간 지원되더라도 프리뷰 API는 다음 릴리스에서 변경될 수 있습니다. 운영 규칙에는 정식 기능만 사용하고 프리뷰는 격리된 실험으로 유지합니다.
연습 문제
프리뷰 여부에 따라 javac와 java 인수를 정확히 생성하세요.
컴파일에는 릴리스 25와 린트 옵션이, 실행에는 --enable-preview가 필요합니다.
해설 보기
import java.util.List;
public final class PreviewCommandPolicySolution {
static List<String> compile(String file, boolean preview) {
if (preview) {
return List.of("javac", "--enable-preview", "--release", "25", "-Xlint:preview", file);
}
return List.of("javac", "--release", "25", file);
}
static List<String> run(String className, boolean preview) {
if (preview) {
return List.of("java", "--enable-preview", className);
}
return List.of("java", className);
}
public static void main(String[] args) {
System.out.println(compile("StableLab.java", true));
System.out.println(run("StableLab", true));
System.out.println(compile("Application.java", false));
}
}종료 기준은 안정 명령에 프리뷰 플래그가 없고 프리뷰 컴파일·실행 양쪽에는 플래그가 있는 것입니다.