본문 바로가기

기본형 패턴과 switch

Java 25 프리뷰의 기본형 패턴과 switch를 실행해 정확한 축소 변환, 값 적합성 검사와 정보 손실의 경계를 확인합니다.

JEP 507은 Java 25에서 패턴의 기본형 타입, instanceof, switch를 세 번째 프리뷰로 제공합니다.

int instanceof byte small은 형변환 가능 여부만 보는 것이 아니라 현재 값을 정보 손실 없이 byte로 변환할 수 있는지 검사하고 바인딩합니다.

long·float·double·boolean도 switch 선택자로 사용할 수 있습니다.

모든 소스는 프리뷰 컴파일·실행 플래그가 필요합니다.


패턴 검사와 강제 축소 변환

기본형 패턴의 목적은 안전하고 정확한 변환을 확인하는 것입니다.

아래 코드는 큰 long을 먼저 int로 형변환해 정보를 잃고 난 뒤 패턴 switch를 수행합니다.

4,294,967,297은 int 1로 잘려 출력이 wrong-positive=1이 됩니다.

preview/PrimitiveNarrowingBeforePatternBug.java
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 분기를 유지하는 것입니다.

예시

원래 값은 2의 32제곱 + 1입니다. 앞의 강제 int 변환은 상위 비트를 버려 낮은 32비트에 1만 남깁니다. 이후 분기가 읽는 입력은 이미 1이므로 그 값이 양수라는 판단은 원래 4,294,967,297을 보존했다는 뜻이 아닙니다. 실제 조회 수의 단위나 음수 허용 여부도 정확한 타입 변환과 별개의 업무 조건입니다.


instanceof 기본형 패턴

전통적으로 범위 확인 뒤 형변환을 별도 작성해야 했습니다.

프리뷰 패턴은 현재 int 값이 byte로 정확히 표현될 때만 바인딩을 만듭니다.

preview/ExactBytePatternProbe.java
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));
    }
}

다음은 원문 입력에 Java 25 프리뷰의 정확한 변환 규칙을 적용한 결과입니다.

원문 네 정수의 byte 패턴 판정

원문 코드와 Java 25 명세에 따른 조건과 결과를 비교합니다.

Java 25 프리뷰 · byte 범위의 양쪽 경계

원문 네 정수의 byte 패턴 판정

byte가 정확히 담는 범위는 −128부터 127까지입니다. 범위 밖의 입력은 int 분기로 남아 원래 값을 유지합니다.

int 입력

127

byte로 정확한 변환
가능
원문의 반환값
byte:127

int 입력

128

byte로 정확한 변환
불가능
원문의 반환값
int:128

int 입력

-128

byte로 정확한 변환
가능
원문의 반환값
byte:-128

int 입력

-129

byte로 정확한 변환
불가능
원문의 반환값
int:-129

instanceof byte 검사 형태도 가능하지만 바인딩 형태가 형변환 중복을 없앱니다.

javac --enable-preview --release 25 -Xlint:preview ExactBytePatternProbe.java
java --enable-preview ExactBytePatternProbe
# byte:127
# int:128
# byte:-128
# int:-129

long·boolean switch 분기

Java 25 프리뷰는 이전 switch 선택자 제한을 넓힙니다.

상수 레이블 타입은 선택자와 맞춰 0L처럼 씁니다.

boolean switch는 삼항 연산자보다 여러 문장이 필요한 분기를 식으로 구성할 수 있습니다.

preview/ExpandedPrimitiveSwitch.java
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 패턴으로 갑니다.

preview/PrimitiveRecordPatternConversion.java
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)));
    }
}

정확한 변환 실험표

기본형 변환은 단순 최솟값·최댓값 범위만이 아니라 부동소수점이 정수를 정확히 표현하는지, NaN·무한대·부호 있는 0을 어떻게 다루는지 확인해야 합니다.

운영 파서를 프리뷰 패턴 하나로 바꾸기 전 기존 입력 검사 의미와 비교합니다.

preview/PrimitivePatternBoundaryTable.java
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));
        }
    }
}

아래 표는 실제 실행 기록이 아니라 원문 입력과 Java 25 프리뷰 명세에 따른 분기입니다.

double에서 int로의 정확한 변환 경계

원문 코드와 Java 25 명세에 따른 조건과 결과를 비교합니다.

Java 25 프리뷰 · 값과 부호까지 보존하는 판정

double에서 int로의 정확한 변환 경계

int 범위 안의 정수라도 원래 정보를 보존해야 합니다. −0.0은 부호를 잃고, 소수·범위 초과·NaN·무한대도 int 패턴과 일치하지 않습니다.

double에서 int로의 정확한 변환 경계
원문 입력int 패턴이유
0.0 · 42.0일치정수 값 보존
Integer.MAX_VALUE일치int 최댓값 보존
-0.0불일치음의 0 부호 손실
42.25불일치소수부 손실
MAX_VALUE + 1.0불일치int 범위 초과
NaN · +Infinity불일치정수로 정확히 표현할 수 없음
0.0 · 42.0
int 패턴
일치
이유
정수 값 보존
Integer.MAX_VALUE
int 패턴
일치
이유
int 최댓값 보존
-0.0
int 패턴
불일치
이유
음의 0 부호 손실
42.25
int 패턴
불일치
이유
소수부 손실
MAX_VALUE + 1.0
int 패턴
불일치
이유
int 범위 초과
NaN · +Infinity
int 패턴
불일치
이유
정수로 정확히 표현할 수 없음

프리뷰 명세가 다음 릴리스에서 바뀌면 같은 점검 프로그램을 다시 실행합니다.


적용 판단

  • 수치 파서와 직렬화 경계의 명시적 입력 검사를 대체하기 전에 오류 메시지 요구 사항을 확인합니다.
  • 정확한 변환 분기가 기존 형변환의 절삭·반올림과 다른지 비교합니다.
  • long·boolean switch가 복잡한 if보다 실제로 읽기 좋은지 검토합니다.
  • 안정 산출물에는 프리뷰 문법을 넣지 않고 실험 결과만 설계 문서에 반영합니다.
  • 다음 JDK 릴리스에서는 해당 버전의 명세와 마이그레이션 요구를 다시 확인합니다.
instanceof 기본형 패턴이 일치하면 모든 수치 변환이 안전한가요?

해당 소스 값을 대상 타입으로 정확하게 변환할 수 있다는 뜻입니다.

업무 범위, 단위, 오버플로 이후 이미 손실된 값까지 보장하지 않습니다.

원본을 먼저 형변환하거나 계산에서 오버플로가 난 뒤 검사하면 패턴은 잃어버린 정보를 복구하지 못합니다.


연습 문제

0, 양수 int 범위, int를 넘는 양수, 음수를 분리하세요.

원본 long을 미리 형변환하지 않습니다.

입력은 45, 2,147,483,648, −1입니다. 각각 사용할 레이블과 바인딩 타입을 먼저 적습니다. 추가로 0과 2,147,483,647을 넣어 양수 경계와 int 최댓값이 어느 분기로 가는지 예상합니다. 이 입력들을 실제 프리뷰 프로그램에 넣을 때는 JDK 25 컴파일·실행 플래그를 그대로 유지합니다.

해설 보기
preview/LongViewCountClassificationSolution.java
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이며 컴파일·실행 모두 프리뷰 플래그를 사용합니다.

0은 맨 앞 상수 분기에서 zero, int 최댓값은 정확히 들어가고 양수이므로 compact:2147483647입니다. −1도 int에는 들어가지만 양수 가드를 통과하지 못하므로 최종 음수 분기가 처리합니다. 타입 변환 성공과 업무상 유효한 양수 여부가 서로 다른 검사입니다.