기본형 패턴과 switch
Java 25 프리뷰의 기본형 패턴과 switch를 실행해 정확한 축소 변환, 값 적합성 검사와 정보 손실의 경계를 확인합니다.
JEP 507은 Java 25에서 패턴의 기본형 타입, instanceof, switch를 세 번째 프리뷰로 제공합니다.
int instanceof byte small은 형변환 가능 여부만 보는 것이 아니라 현재 값을 정보 손실 없이 byte로 변환할 수 있는지 검사하고 바인딩합니다.
long·float·double·boolean도 switch 선택자로 사용할 수 있습니다.
모든 소스는 프리뷰 컴파일·실행 플래그가 필요합니다.
먼저 narrowing cast하면 원본 값이 잘린 뒤라 pattern이 이미 잃은 정보를 복구할 수 없습니다.
- (int) original
항상 변환 · overflow/truncation 뒤에 검사
- original instanceof int exact
정확 변환 가능할 때만 bind · 아니면 fallback
패턴 검사와 강제 축소 변환
기본형 패턴의 목적은 안전하고 정확한 변환을 확인하는 것입니다.
아래 코드는 큰 long을 먼저 int로 형변환해 정보를 잃고 난 뒤 패턴 switch를 수행합니다.
4,294,967,297은 int 1로 잘려 출력이 wrong-positive=1이 됩니다.
public final class PrimitiveNarrowingBeforePatternBug {
static String classify(long original) {
int narrowed = (int) original;
return switch (narrowed) {
case int value when value > 0 -> "positive:" + value;
case int value -> "other:" + value;
};
}
public static void main(String[] args) {
long original = 4_294_967_297L;
System.out.println("original=" + original);
System.out.println("wrong-positive=" + classify(original).substring("positive:".length()));
}
}원칙은 원래 넓은 타입을 패턴 검사 대상으로 유지하고 instanceof int converted처럼 정확한 변환이 성공했을 때만 좁은 바인딩을 사용하거나 long 분기를 유지하는 것입니다.
instanceof 기본형 패턴
전통적으로 범위 확인 뒤 형변환을 별도 작성해야 했습니다.
프리뷰 패턴은 현재 int가 byte로 정확히 표현될 때만 바인딩을 만듭니다.
public final class ExactBytePatternProbe {
static String encode(int value) {
if (value instanceof byte exact) {
return "byte:" + exact;
}
return "int:" + value;
}
public static void main(String[] args) {
System.out.println(encode(127));
System.out.println(encode(128));
System.out.println(encode(-128));
System.out.println(encode(-129));
}
}관찰 결과 127과 -128만 byte 분기를 사용합니다.
instanceof byte 검사 형태도 가능하지만 바인딩 형태가 형변환 중복을 없앱니다.
정확성 규칙은 소스 타입, 대상 타입, 실행 값에 따라 달라지므로 경계값 표로 확인합니다.
javac --enable-preview --release 25 -Xlint:preview ExactBytePatternProbe.java
java --enable-preview ExactBytePatternProbe
# byte:127
# int:128
# byte:-128
# int:-129byte pattern은 type 이름만 검사하지 않고 현재 정수 값이 -128..127 범위에 정확히 들어오는지 확인합니다.
- -129
int fallback
- -128
byte match
- 0
byte match
- 127
byte match
- 128
int fallback
long·boolean switch 분기
Java 25 프리뷰는 이전 switch 선택자 제한을 넓힙니다.
상수 레이블 타입은 선택자와 맞춰 0L처럼 씁니다.
boolean switch는 삼항 연산자보다 여러 문장이 필요한 분기를 식으로 구성할 수 있습니다.
public final class ExpandedPrimitiveSwitch {
static String bucket(long value) {
return switch (value) {
case 0L -> "zero";
case 1L -> "one";
case long number when number > 1_000_000_000L -> "large";
case long number when number > 0 -> "positive";
case long ignored -> "negative";
};
}
static String access(boolean loggedIn) {
return switch (loggedIn) {
case true -> "member";
case false -> "guest";
};
}
public static void main(String[] args) {
System.out.println(bucket(0L));
System.out.println(bucket(5_000_000_000L));
System.out.println(bucket(-3L));
System.out.println(access(true));
System.out.println(access(false));
}
}일반 case long을 가드가 있는 case보다 앞에 두면 뒤 case에는 도달할 수 없습니다.
안정 패턴 switch에서 배운 위에서 아래 선택 규칙이 기본형 패턴에도 적용됩니다.
record 패턴의 변환 규칙
record 구성 요소가 double이어도 현재 값을 int로 정확히 변환할 수 있으면 int whole 패턴과 일치합니다.
소수부가 있거나 범위를 벗어나거나 표현 손실이 발생하는 값은 대체 경로인 double 패턴으로 갑니다.
public final class PrimitiveRecordPatternConversion {
private record Measurement(double value) {}
static String render(Measurement measurement) {
return switch (measurement) {
case Measurement(int whole) -> "whole:" + whole;
case Measurement(double decimal) -> "decimal:" + decimal;
};
}
public static void main(String[] args) {
System.out.println(render(new Measurement(25.0)));
System.out.println(render(new Measurement(25.5)));
System.out.println(render(new Measurement(3_000_000_000.0)));
}
}소수부가 없다는 사실만으로 int 범위와 표현 정확성까지 보장되는 것은 아닙니다.
- 25.0
Measurement(int whole) → 25
- 25.5
Measurement(double decimal)
- 3_000_000_000.0
int 범위 밖 → double
- NaN/Infinity
double fallback
정확한 변환 실험표
기본형 변환은 단순 최솟값·최댓값 범위만이 아니라 부동소수점이 정수를 정확히 표현하는지, NaN·무한대·부호 있는 0을 어떻게 다루는지 확인해야 합니다.
운영 파서를 프리뷰 패턴 하나로 바꾸기 전 기존 입력 검사 의미와 비교합니다.
public final class PrimitivePatternBoundaryTable {
static String asInt(double value) {
if (value instanceof int exact) {
return "int:" + exact;
}
return "double:" + value;
}
public static void main(String[] args) {
double[] values = {
0.0,
-0.0,
42.0,
42.25,
Integer.MAX_VALUE,
(double) Integer.MAX_VALUE + 1,
Double.NaN,
Double.POSITIVE_INFINITY
};
for (double value : values) {
System.out.println(asInt(value));
}
}
}예상 산출물에 부동소수점 텍스트 전체를 고정하기보다 어느 case가 int인지 표로 기록합니다.
프리뷰 명세가 다음 릴리스에서 바뀌면 같은 점검 프로그램을 다시 실행합니다.
대표 양수 하나만 성공시키면 부동소수점의 NaN·무한대·부호 있는 0과 exact integer 경계를 놓칩니다.
| 입력 군 | 예시 | 확인 |
|---|---|---|
| 정수 경계 | MIN-1·MIN·MAX·MAX+1 | target match |
| 소수 | 25.5·-0.5 | fallback 유지 |
| 큰 exact double | 3_000_000_000.0 | int 범위 |
| 특수값 | NaN·±Infinity·-0.0 | 명세 분기 |
| 회귀 | 기존 parser fixture | 오류 message |
적용 판단
- 수치 파서와 직렬화 경계의 명시적 입력 검사를 대체하기 전에 오류 메시지 요구 사항을 확인합니다.
- 정확한 변환 분기가 기존 형변환의 절삭·반올림과 다른지 비교합니다.
long·booleanswitch가 복잡한if보다 실제로 읽기 좋은지 검토합니다.- 안정 산출물에는 프리뷰 문법을 넣지 않고 실험 결과만 설계 문서에 반영합니다.
- JDK 26의 네 번째 프리뷰에서 변화한 명세와 마이그레이션을 다시 확인합니다.
instanceof 기본형 패턴이 일치하면 모든 수치 변환이 안전한가요?
해당 소스 값을 대상 타입으로 정확하게 변환할 수 있다는 뜻입니다.
업무 범위, 단위, 오버플로 이후 이미 손실된 값까지 보장하지 않습니다.
원본을 먼저 형변환하거나 계산에서 오버플로가 난 뒤 검사하면 패턴은 잃어버린 정보를 복구하지 못합니다.
연습 문제
0, 양수 int 범위, int를 넘는 양수, 음수를 분리하세요.
원본 long을 미리 형변환하지 않습니다.
해설 보기
public final class LongViewCountClassificationSolution {
static String classify(long viewCount) {
return switch (viewCount) {
case 0L -> "zero";
case int exact when exact > 0 -> "compact:" + exact;
case long value when value > 0 -> "large:" + value;
case long value -> "invalid:" + value;
};
}
public static void main(String[] args) {
System.out.println(classify(45L));
System.out.println(classify((long) Integer.MAX_VALUE + 1));
System.out.println(classify(-1L));
}
}종료 기준은 compact:45, large:2147483648, invalid:-1이며 컴파일·실행 모두 프리뷰 플래그를 사용합니다.