선택 심화: 프리뷰 소스 격리
필수 입문 과정을 마친 학습자가 Java 25 프리뷰 기능의 플래그, 소스 세트와 산출물 격리를 선택적으로 실험합니다.
이 장은 선택 심화 과정입니다.
Java 문법, 객체지향, 컬렉션, 예외, 파일, 네트워크와 JVM 실행 원리를 먼저 익힌 뒤 관심이 있을 때 진행하세요.
이후 핵심 장은 이 장의 프리뷰 기능을 안다고 가정하지 않으므로 건너뛰어도 자습 흐름에 문제가 없습니다.
프리뷰 기능은 완성된 Java SE 규칙이 아니라 개발자가 피드백을 제공하도록 공개한 제한적 기능입니다.
Java 25의 Stable Values, 구조화된 동시성, 기본형 패턴은 프리뷰이며 기본적으로 비활성입니다.
프리뷰 API나 문법은 다음 릴리스에서 바뀌거나 사라질 수 있습니다. Java 25 프리뷰 기능을 사용한 클래스 파일은 Java 25 런타임에서 프리뷰를 허용해 실행해야 합니다.
따라서 안정적인 애플리케이션 소스와 같은 빌드 경로에 섞지 않습니다.
프리뷰 플래그 없는 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 대상을 함께 고정합니다.
JDK 25 소스 실행기는 java --enable-preview File.java처럼 --source 25를 생략해도 현재 런타임 버전을 적용합니다. 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를 지정합니다. 프리뷰 소스 컴파일에는 프리뷰·릴리스 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));
}
}안정 코드와 프리뷰 코드의 명령 경계
| 만들 명령 | 필요한 옵션 | 풀이가 하지 않는 일 |
|---|---|---|
| 안정 소스 컴파일 | --release 25 | 프로세스를 실행하거나 컴파일 결과를 확인하지 않음 |
| 프리뷰 소스 컴파일 | --enable-preview --release 25 -Xlint:preview | 실행 JDK의 버전이나 산출물 격리를 검사하지 않음 |
| 프리뷰 클래스 실행 | --enable-preview | 메인 클래스 존재·의존성·JDK 25 일치를 검사하지 않음 |
- 안정 소스 컴파일
- 필요한 옵션:
--release 25풀이가 하지 않는 일: 프로세스를 실행하거나 컴파일 결과를 확인하지 않음 - 프리뷰 소스 컴파일
- 필요한 옵션:
--enable-preview--release 25-Xlint:preview풀이가 하지 않는 일: 실행 JDK의 버전이나 산출물 격리를 검사하지 않음 - 프리뷰 클래스 실행
- 필요한 옵션:
--enable-preview풀이가 하지 않는 일: 메인 클래스 존재·의존성·JDK 25 일치를 검사하지 않음
PreviewCommandPolicySolution은 인수 목록을 만드는 예제입니다. Java 25 프리뷰 클래스는 Java 25 런타임과 프리뷰 허용이 필요하며, 실제 컴파일·실행 성공을 이 목록만으로 판정할 수는 없습니다.