CodeGym /행동 /JAVA 25 SELF /switch에서의 패턴 매칭 (Java 17/21+)

switch에서의 패턴 매칭 (Java 17/21+)

JAVA 25 SELF
레벨 65 , 레슨 2
사용 가능

1. 전통적인 switch: 과거의 한계

패턴 매칭이 도입되기 전 Java의 switch가 어땠는지 떠올려 봅시다. 솔직히 꽤 보수적이었습니다. (int, char, 이후에는 enum, String) 같은 프리미티브에만 동작했고, 서로 다른 객체 타입을 처리하려면 길고 장황한 if-else를 작성해야 했습니다.

Object obj = ...;
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println("문자열: " + s);
} else if (obj instanceof Integer) {
    Integer i = (Integer) obj;
    System.out.println("정수: " + i);
} else if (obj == null) {
    System.out.println("null!");
} else {
    System.out.println("다른 것");
}

편할까요? 그렇지 않습니다. 코드는 장황하고 실수하기 쉽습니다. 새 타입이 추가되면 if-else 체인을 다시 작성해야 합니다.

2. switch의 패턴 매칭: Java 17/21+의 혁신

Java 17(프리뷰)에서 도입되어 Java 21+에서 안정화된 새 switch는 패턴 매칭을 지원합니다. 이제 switch는 프리미티브뿐 아니라 임의의 객체와 타입, 심지어 null까지 처리할 수 있으며, 추가 조건(가드 표현식)도 지원합니다.

새 문법: 간결함과 강력함

switch에서의 간단한 패턴 매칭 예:

Object obj = ...;
switch (obj) {
    case String s -> System.out.println("문자열: " + s);
    case Integer i -> System.out.println("정수: " + i);
    case null -> System.out.println("null!");
    default -> System.out.println("다른 것");
}

무슨 일이 일어나나요?

  • obj가 문자열이면 변수 s는 자동으로 String 타입으로 캐스팅됩니다.
  • obj가 정수이면 변수 iInteger가 됩니다.
  • objnull이면 전용 분기가 동작합니다.
  • 그 외는 모두 default로 처리됩니다.

왜 편리할까요?

  • 명시적 캐스팅이 필요 없음: Java가 타입 안전성을 보장합니다.
  • 코드가 줄어듦: 불필요한 괄호, 캐스팅, 중복이 사라집니다.
  • 더 안전함: 컴파일러가 가능한 경우를 놓치지 않았는지 검사합니다(특히 sealed 클래스와 함께 사용할 때 더 효과적입니다).
  • 새 타입 추가가 쉬움: case 분기를 하나 더 추가하면 됩니다.

3. switch의 패턴 매칭: 기본 예제

예제 1: 서로 다른 타입 처리

Object obj = 42;

switch (obj) {
    case String s -> System.out.println("문자열: " + s);
    case Integer i -> System.out.println("정수: " + i);
    case null -> System.out.println("이는 null");
    default -> System.out.println("알 수 없는 타입");
}

출력:

정수: 42

예제 2: 가드 조건(when) 사용

가끔은 타입만 아는 것으로 부족하고, 추가 조건을 넣고 싶을 때가 있습니다.

Object obj = "Hello, world!";

switch (obj) {
    case String s when s.length() > 10 -> System.out.println("긴 문자열: " + s);
    case String s -> System.out.println("짧은 문자열: " + s);
    case Integer i when i > 0 -> System.out.println("양의 정수: " + i);
    case Integer i -> System.out.println("음수 또는 0: " + i);
    default -> System.out.println("다른 것");
}

출력:

긴 문자열: Hello, world!

주의할 점:

  • whencase에 붙는 가드 표현식(추가 검사)입니다.
  • 조건이 만족되지 않으면 다음 case들이 계속 검사됩니다.

예제 3: null 처리

Object obj = null;

switch (obj) {
    case null -> System.out.println("null입니다!");
    default -> System.out.println("null 아님");
}

출력:

null입니다!

4. 클래스와 상속과 함께하는 switch 패턴 매칭

switch의 패턴 매칭은 클래스 계층과 함께 사용할 때 특히 강력합니다.

sealed interface Shape permits Circle, Rectangle, Square {}

final class Circle implements Shape {
    final double radius;
    Circle(double radius) { this.radius = radius; }
}

final class Rectangle implements Shape {
    final double width, height;
    Rectangle(double width, double height) { this.width = width; this.height = height; }
}

final class Square implements Shape {
    final double side;
    Square(double side) { this.side = side; }
}
Shape shape = new Rectangle(2, 3);

String description = switch (shape) {
    case Circle c -> "반지름이 " + c.radius + "인 원";
    case Rectangle r -> "직사각형 " + r.width + "x" + r.height;
    case Square s -> "한 변의 길이가 " + s.side + "인 정사각형";
};
System.out.println(description);

출력:

직사각형 2.0x3.0

핵심: sealed 클래스를 사용하고 모든 변형을 처리했다면 default 분기가 필요 없습니다. 컴파일러가 다른 경우가 없음을 알고 있기 때문입니다!

5. record 클래스와 중첩 패턴과 함께 switch 사용

switch의 패턴 매칭은 record 클래스와 중첩 패턴(Java 21+부터)을 지원합니다.

record Point(int x, int y) {}
record Line(Point start, Point end) {}

Object obj = new Line(new Point(1, 2), new Point(3, 4));

String result = switch (obj) {
    case Line(Point(int x1, int y1), Point(int x2, int y2)) ->
        "선 (" + x1 + "," + y1 + ")에서 (" + x2 + "," + y2 + ")까지";
    case Point(int x, int y) -> "점 (" + x + "," + y + ")";
    default -> "알 수 없는 객체";
};
System.out.println(result);

출력:

선 (1,2)에서 (3,4)까지

6. switch의 패턴 매칭: 특징과 제한

변수는 해당 case 내부에서만 접근 가능

패턴에서 선언된 변수(예: String s)는 해당 case 블록 내부에서만 존재합니다. 그 범위를 벗어나면 접근할 수 없습니다.

switch (obj) {
    case String s -> System.out.println(s); // 여기서는 s를 볼 수 있음
    // System.out.println(s); // 여기서는 컴파일 오류!
}

null 처리

  • 예전에는 객체를 대상으로 하는 switchnull이 전달되면 NullPointerException이 발생했습니다.
  • 새로운 패턴 매칭 switch에서는 case nullnull을 명시적으로 처리할 수 있습니다.

가드 표현식(when)

추가 조건(when)은 expression 스타일의 switch(화살표 문법 ->)에서만 사용할 수 있습니다.

case String s when s.isEmpty() -> System.out.println("빈 문자열");

JDK 및 IDE 요구 사항

  • switch의 패턴 매칭은 Java 17(프리뷰)과 Java 21+(안정화)에서 제공되는 기능입니다.
  • 동작을 위해서는 JDK 21과 새 기능을 지원하는 IDE(예: IntelliJ IDEA 2023+)가 필요합니다.

7. 실제 애플리케이션에서의 패턴 매칭 switch 활용

학습용 애플리케이션—간단한 작업 트래커를 살펴봅시다. 추상적인 작업 타입과 여러 종류가 있다고 가정해 보겠습니다:

sealed interface Task permits SimpleTask, DeadlineTask, MeetingTask {}

final class SimpleTask implements Task {
    final String description;
    SimpleTask(String description) { this.description = description; }
}

final class DeadlineTask implements Task {
    final String description;
    final java.time.LocalDate deadline;
    DeadlineTask(String description, java.time.LocalDate deadline) {
        this.description = description;
        this.deadline = deadline;
    }
}

final class MeetingTask implements Task {
    final String topic;
    final java.time.LocalTime time;
    MeetingTask(String topic, java.time.LocalTime time) {
        this.topic = topic;
        this.time = time;
    }
}

모든 로직을 하나의 간결한 switch로:

Task task = new DeadlineTask("프로젝트 제출", java.time.LocalDate.of(2024, 7, 1));

String info = switch (task) {
    case SimpleTask st -> "일반 작업: " + st.description;
    case DeadlineTask dt -> "마감이 있는 작업: " + dt.description + ", 마감 " + dt.deadline;
    case MeetingTask mt -> "회의: " + mt.topic + " - " + mt.time;
};
System.out.println(info);

출력:

마감이 있는 작업: 프로젝트 제출, 마감 2024-07-01

8. switch의 패턴 매칭을 사용할 때 자주 하는 실수

실수 №1: null 처리를 잊음. objnull일 수 있는데도 case null을 추가하지 않으면 MatchException(또는 예전 switch에서는 NullPointerException)이 발생할 수 있습니다. 가능하다면 항상 null을 명시적으로 처리하세요.

실수 №2: sealed 클래스의 모든 서브타입을 고려하지 않음. sealed 클래스를 사용하면서 switch에서 모든 변형을 처리하지 않으면, 컴파일러가 오류를 내거나 default 분기를 요구합니다. 계층을 확장할 때는 새로운 case를 잊지 말고 추가하세요.

실수 №3: 패턴 변수는 case 블록 밖에서 접근 불가. 패턴에서 선언된 변수는 화살표 블록 내부에서만 존재합니다. 블록 밖에서 사용하려 하면 컴파일 오류가 발생합니다.

실수 №4: 오래된 JDK나 IDE에서 새 기능 사용. switch의 패턴 매칭은 Java 21+과 이를 지원하는 IDE가 필요합니다. 오래된 버전을 사용하면 컴파일 오류가 나거나 코드 인사이트가 부정확할 수 있습니다.

실수 №5: 가드 표현식을 혼동. 예전 블록형 switch에서 when을 사용하려고요? 불가능합니다. 가드는 화살표 문법 ->에서만 사용할 수 있습니다.

실수 №6: 열린 계층에서는 default 분기를 빼먹음. sealed 클래스만 다루는 것이 아니라면 반드시 default를 추가해 모든 경우를 처리해야 합니다.

1
과제
JAVA 25 SELF, 레벨 65, 레슨 2
잠금
분석 시스템을 위한 숫자 분류기 🔢
분석 시스템을 위한 숫자 분류기 🔢
1
과제
JAVA 25 SELF, 레벨 65, 레슨 2
잠금
동물병원을 위한 스마트 반려동물 식별 시스템 🐕🐈🦜
동물병원을 위한 스마트 반려동물 식별 시스템 🐕🐈🦜
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION