티스토리 뷰

스위치 표현식(switch expressions)에 대한 JEP 명세는 https://openjdk.java.net/jeps/361에서 확인할 수 있다. (해당 URL에 있는 예제를 이 블로그 포스트에서 활용하였다.)

 

스위치 표현식이라고 제목을 달았지만, 프로그래밍 언어의 분기 표현중 하나인 스위치 문장(switch statements)에 대한 기능 확장이라고 이해하면 된다. 기존의 switch는 분기하기 위해 case 문장에 고정된 값을 지정하고 여기에 맞는 코드를 작성하지만 개선된 스위치 표현식에서는 여러개의 조건에 따라 값을 처리하고 결과를 리턴할 수 있도록 개선되었다.

 

JDK 12와 13에서 프리뷰 버전으로 소개되었고 14에서 정식으로 포함되었기 때문에 기능을 문제 없이 사용하려면 JDK 14 이상을 이용해야 한다.

 

자바의 switch 문장은 C와 C++ 의 문법 구조를 차용하였고 큰 변화없이 현재까지 사용하였다. 가장 일반적인 형태는 아래와 같이 정수형 값에 따라 case 문으로 분기하는 형태이다.

switch (caseType) {
case 1:
	break;
case 2:
	break;
case 3:
	break;
default:
	break;
}

 

자바 7에서 switch 문장에 추가된 것은 기존에는 정수만 사용 가능하였지만 문자열도 사용이 가능해졌다. 구분할 수 있는 타입에 문자열이 추가되었을 뿐 문법 상으로나 사용법 상으로는 동일하다.

// 자바 7 이상에서 컴파일 된다.
String caseStrType = "modern";

switch (caseStrType) {
case "modern":
	break;
case "old":
	break;
default:
	break;
}

그렇다면, 기존 우리가 사용하고 있는 switch 문장의 문제점은 무엇일까? 유명한 마틴 파울러의 Refactoring 책에 의하면 switch 문장은 구린내 나는 코드로 정의하였고 switch 문장 대신 재정의를 통해 case로 구분하는 일이 없도록 하라고 권하고 있다. 이러한 소프트웨어의 설계적인 측면을 벗어나서 순수한 코드로써 문제점을 예제를 통해 살펴보자.

 

1. 새로운 switch 문장은 하나의 case 문장에 여러개의 조건을 담을 수 있다.

switch (day) {
case MONDAY:
case FRIDAY:
case SUNDAY:
	System.out.println(6);
	break;
case TUESDAY:
	System.out.println(7);
	break;
case THURSDAY:
case SATURDAY:
	System.out.println(8);
	break;
case WEDNESDAY:
	System.out.println(9);
	break;

위의 코드는 break 문장의 위험성과 중복된 case 조건의 복잡성을 보여준다. 하나의 case 에는 오직 하나의 조건만이 들어가기 때문에 여러 조건에 걸쳐 처리를 하기 위해서는 break 문장을 이용해서 코드를 작성해야 한다. 예를 들어 MONDAY, FRIDAY, SUNDAY에 같은 결과 처리를 위해 MONDAY와 FRIDAY에는 break 문장을 생략하고 SUNDAY에서 그 모든 것을 처리하도록 코딩했다. 하지만 이는 코드를 이해하기 어렵게 만들고 break 문장의 잘못으로 버그가 발생할 수 있으며 나중에 문제시 디버깅하기도 어려운 코드가 되어 버린다.

이를 해결하기 위한 새로운 코드는 다음과 같다.

switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
    case TUESDAY                -> System.out.println(7);
    case THURSDAY, SATURDAY     -> System.out.println(8);
    case WEDNESDAY              -> System.out.println(9);
}

위와 같이 "case 조건들 -> " 문장을 이용하면 여러개의 조건을 콤마를 이용해서 기술할 수 있고 코드의 가독성 역시 매우 좋아진다. 단, 주의할 점은 새로운 switch 표현식에는 break를 사용할 수 없으며 사용할 경우 컴파일 에러가 발생한다.

 

2. switch 에서 값을 리턴할 수 있다.

기존 switch 문장을 case, break, default 를 이용해서 로직을 처리하는 것으로 끝난다. 그래서 case에 따라 특정 값을 설정하려면 다음과 같이 코딩 해야 한다.

int numLetters;
switch (day) {
    case MONDAY:
    case FRIDAY:
    case SUNDAY:
        numLetters = 6;
        break;
    case TUESDAY:
        numLetters = 7;
        break;
    case THURSDAY:
    case SATURDAY:
        numLetters = 8;
        break;
    case WEDNESDAY:
        numLetters = 9;
        break;
    default:
        throw new IllegalStateException("Wat: " + day);
}

day의 case에 따라 numLetters의 값을 설정하는 코드이다. switch 블록 내에서는 변수 선언이 불가능하기 때문에 외부에서 선언한 변수에 의존하게 된다. 또한 switch 문장 내부에서 외부 변수 값을 조작하게 되어, 에러지향적인 요소가 된다.

개선된 switch expressions에서는 다음과 같이 코드를 변경할 수 있다.

// switch 문장이 값을 리턴
int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY                -> 7;
    case THURSDAY, SATURDAY     -> 8;
    case WEDNESDAY              -> 9;
    default -> 0; // 값을 리턴할 경우 default 문장이 반드시 있어야 한다.
};

값을 리턴할 경우 주의할 점은 default 조건이 반드시 있어야 한다. 값을 리턴하지 않을 경우는 조건에 맞지 않으면 수행하지 않고 종료하면 되지만 리턴의 의미는 경우에 맞지 않더라도 반드시 값을 리턴해야 하기 때문이다.

 

3. yield 키워드

스위치 표현식 및 스위치 문장에서 값을 명시적으로 리턴하기 위해 새로운 키워드인 yield를 도입하였다. 예제를 통해 사용해야 하는 경우를 알아보겠다.

 

대부분의 경우 case에 대한 처리는 하나의 문장으로 처리하지만 여러 문장으로 작성해야 하는 경우도 있다. 기존 스위치 문장과는 다르게 여러문장으로 case 문장을 작성하기 위해서는 {} 으로 코드 블록을 정의해야 한다.

아래 예제는 새로운 스위치 표현식의 default 코드 블록에서 값을 리턴하기 위해 yield 키워드를 사용한 것이다.

// 스위치 표현식에서 int 값을 리턴하는 예
int j = switch (day) {
    case MONDAY  -> 0;
    case TUESDAY -> 1;
    default      -> {
        int k = day.toString().length();
        int result = f(k);
        yield result;
    }
};

위의 예제에서 주의할 점은 코드 블록 내에서 값을 리턴하기 위한 용도가 아닌 경우 컴파일 에러가 발생한다. 예를 들어 다음과 같이 case 문장을 작성하면 안된다.

 

case MONDAY -> yield 0; // 컴파일 에러. 스위치 표현식에서는 코드 블록내에서만 사용 가능하다.

 

yield 키워드는 스위치 표현식 외에도 기존 스위치 문장에서도 다음 예제와 같이 사용할 수 있다.

// 스위치 문장에서 값 리턴 예
int result = switch (s) {
    case "Foo": 
        yield 1;
    case "Bar":
        yield 2;
    default:
        System.out.println("Neither Foo nor Bar, hmmm...");
        yield 0;
};

스위치 문장에서의 yield는 코드 블록 여부와는 상관 없이 사용할 수 있는데 그 이유는 "case 문장 : " 자체가 여러 라인의 코드를 작성할 수 있는 블록으로 인식하기 때문이다. 그리고 yield 키워드를 사용할 경우 break 문장이 없어서 스위치 문장이 종료됨으로 기존과 다르게 동작함을 인식해야 한다.

댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함