1. 개선된 switch 문이란?

2020년 3월에 출시된 Java 14부터 개선된 switch 문을 지원합니다.

기존 switch문은 깔끔하지 못하고 가독성도 떨어지며, break문의 누락으로 인한 오류 가능성도 크기 때문에

화살표 case 라벨, 다중 case 라벨, switch 연산식, yield 예약어 등의 기능이 추가되었습니다.

 

예제에서 사용하는 열거 타입은 다음과 같습니다. 열거 타입을 사용하는 이유는 마지막에 설명드리겠습니다.

enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }

 

 

 

2. 화살표 case 라벨, 다중 case 라벨 (switch1, enhancedSwitch1)

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;
}

day의 길이를 출력하는 기존 switch 문 예제입니다.

day가 MONDAY, FRIDAY, SUNDAY인 경우 fall through를 이용하여 6을 출력하는 것을 볼 수 있습니다.

이를 화살표 case 라벨과 다중 case 라벨을 사용한 개선된 switch문으로 바꾸면 아래와 같습니다.

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);
}

fall through를 사용하지 않고 다중 case 라벨(,)을 사용하여 case MONDAY, FRIDAY, SUNDAY라고 표현한 것이 훨씬 가독성이 좋습니다.

또, 화살표 case 라벨(->)을 사용하면 마지막에 break를 사용한 것과 동일합니다.

이때 주의해야 할 점은 case 라벨(:)을 사용할 때는 실행문이 여러 개여도 중괄호({})로 묶어줄 필요가 없었지만,

화살표 case 라벨(->)을 사용할 때 실행문이 여러 개(즉, 2줄 이상)일 때는 중괄호로 묶어줘야 합니다.

한마디로 화살표 case 라벨(->)을 사용할 때는 if 문이나 for 문처럼 실행문이 1줄일 때만 중괄호를 생략할 수 있습니다.

 

 

 

3. switch 연산식 (switch2, enhancedSwitch2)

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:
        numLetters = -1;
}

numLetters 변수에 day의 길이를 저장하는 기존 switch 문 예제입니다.

case 문에서 numLetters 변수에 값을 저장하는 방식을 사용해야 합니다.

이를 switch 연산식을 사용한 향상된 switch 문으로 바꾸면 아래와 같습니다.

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY	-> 6;
    case TUESDAY				-> 7;
    case THURSDAY, SATURDAY		-> 8;
    case WEDNESDAY				-> 9;
};

화살표 case 라벨(->) 뒤에 있는 값을 return하여 numLetters에 바로 대입할 수 있습니다.

만약 case TUESDAY -> 7;에서 7을 System.out.println(7)이나 "Seven" 등으로 바꾸면 int type이 아니므로 에러가 발생합니다.

numLetters의 type을 Object로 바꿔주면 "Seven"은 가능하지만,

System.out.println(7)은 return type이 void이므로 여전히 에러가 발생합니다.

즉, switch 연산식을 사용할 때 화살표 case 라벨(->) 뒤에 있는 값이 void type이면 에러가 발생합니다.

 

 

 

4. yield 예약어 사용 (enhancedSwitch3, enhancedSwitch4)

yield는 키워드가 아니라 제한된 식별자(restricted identifier)이기 때문에 var처럼 식별자로 사용할 수 있습니다.

방금 전에 본 switch 연산식에서 길이를 return하기 전에 특정 메시지를 출력하고 싶으면 아래와 같이 사용하면 됩니다.

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY	-> {
        System.out.print("Six ");
        yield 6;
    }
    case TUESDAY				-> 7;
    case THURSDAY, SATURDAY		-> 8;
    case WEDNESDAY				-> 9;
};

향상된 switch 문에서는 중괄호 안에서만 yield 예약어를 사용할 수 있습니다.

따라서 case TUESDAY -> yield 7; 이라고 하면 에러입니다. 

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY:
        System.out.print("Six ");
        yield 6;
    case TUESDAY:
        yield 7;
    case THURSDAY, SATURDAY:
        yield 8;
    case WEDNESDAY:
        yield 9;
};

yield 예약어는 case 라벨(:)에도 사용 가능합니다.

case 라벨(:)은 실행문이 여러 개일 때 중괄호를 사용하지 않아도 됩니다.

 

 

 

5. 열거 타입(enum)을 사용하는 이유

static int switchError(String s) {
    return switch (s) {
        case "MONDAY", "FRIDAY", "SUNDAY"	-> 6;
        case "TUESDAY"					-> 7;
        case "THURSDAY, SATURDAY"		-> 8;
        case "WEDNESDAY"				-> 9;
        default						-> -1;
    };
}

위의 메서드에서 default를 주석 처리하면 "'switch' expression does not cover all possible input values" 에러 메시지를 볼 수 있습니다.

가능한 모든 값에 대하여 일치하는 case 라벨이 하나라도 없으면 위와 같은 에러를 볼 수 있습니다.

위의 예제에서는 String type이므로 가능한 모든 값은 거의 무한대나 마찬가지입니다.

String대신 int type이고, default를 사용하지 않는다면 int 범위의 모든 값에 대하여 case를 사용해야 할 것입니다.

따라서 default를 사용하거나, 필요한 값들로 열거 타입(enum)을 만들어서 사용해야 합니다.

 

 

 

6. 전체 코드

예제에서 사용한 전체 코드입니다.

더보기
public class SwitchTest {

    public static void main(String[] args) {
        switch1(Day.FRIDAY);                                // 6
        enhancedSwitch1(Day.FRIDAY);                        // 6

        System.out.println(switch2(Day.SATURDAY));          // 8
        System.out.println(enhancedSwitch2(Day.SATURDAY));  // 8

        System.out.println(enhancedSwitch3(Day.SUNDAY));    // Six 6
        System.out.println(enhancedSwitch4(Day.SUNDAY));    // Six 6

        switchError("ERROR");                            // -1
    }

    static void switch1(Day day) {
        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;
        }
    }

    static void enhancedSwitch1(Day day) {
        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);
        }
    }

    static int switch2(Day day) {
        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:
                numLetters = -1;
        }
        return numLetters;
    }

    static int enhancedSwitch2(Day day) {
        // 변수를 선언하지 않고 바로 return하는 것도 가능
        return switch (day) {
            case MONDAY, FRIDAY, SUNDAY -> 6;
            case TUESDAY                -> 7;
            case THURSDAY, SATURDAY     -> 8;
            case WEDNESDAY              -> 9;
        };

//        int numLetters = switch (day) {
//            case MONDAY, FRIDAY, SUNDAY -> 6;
//            case TUESDAY                -> 7;
//            case THURSDAY, SATURDAY     -> 8;
//            case WEDNESDAY              -> 9;
//        };
//        return numLetters;
    }

    // return type이 Object인 경우 다양한 type 사용 가능
//    static Object enhancedSwitch2(Day day) {
//        return switch (day) {
//            case MONDAY, FRIDAY, SUNDAY -> 6;
//            case TUESDAY                -> "Seven";
//            case THURSDAY, SATURDAY     -> 8;
//            case WEDNESDAY              -> 9;
//        };
//    }

    static int enhancedSwitch3(Day day) {
        return switch (day) {
            case MONDAY, FRIDAY, SUNDAY -> {
                System.out.print("Six ");
                yield 6;
            }
            case TUESDAY -> 7;
            case THURSDAY, SATURDAY -> 8;
            case WEDNESDAY -> 9;
        };
    }

    static int enhancedSwitch4(Day day) {
        return switch (day) {
            case MONDAY, FRIDAY, SUNDAY:
                System.out.print("Six ");
                yield 6;
            case TUESDAY:
                yield 7;
            case THURSDAY, SATURDAY:
                yield 8;
            case WEDNESDAY:
                yield 9;
        };
    }

    static int switchError(String s) {
        return switch (s) {
            case "MONDAY", "FRIDAY", "SUNDAY"   -> 6;
            case "TUESDAY"                      -> 7;
            case "THURSDAY, SATURDAY"           -> 8;
            case "WEDNESDAY"                    -> 9;
            default                             -> -1;
        };
    }

    enum Day { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }
}

 

 

 

7. 참고한 사이트

 

JEP 361: Switch Expressions

JEP 361: Switch Expressions AuthorGavin BiermanOwnerJan LahodaTypeFeatureScopeSEStatusClosed / DeliveredRelease14Componentspecification / languageDiscussionamber dash dev at openjdk dot java dot netEffortSDurationMRelates toJEP 354: Switch Expressi

openjdk.java.net

 

'Java' 카테고리의 다른 글

[Java 10] 자바 var  (0) 2020.09.13

1. var란?

2018년 3월에 출시된 Java 10부터 var를 지원합니다.

 

var i = 1; // int로 추론
var str = "Java"; // String으로 추론
var list = new ArrayList<String>(); // ArrayList<String>으로 추론

var는 지역변수를 선언할 때 초깃값을 통하여 데이터 타입을 추론합니다.

 

int var = 1; // 식별자로 사용 가능

var는 예약된 타입 이름으로 키워드가 아니기 때문에 식별자로 사용할 수 있습니다.

예제만으로는 이해하기 힘들 것 같아서 아래에 JEP 286: Local-Variable Type Inference에서 가져온 관련 내용을 덧붙입니다.

The identifier var is not a keyword; instead it is a reserved type name. This means that code that uses var as a variable, method, or package name will not be affected; code that uses var as a class or interface name will be affected (but these names are rare in practice, since they violate usual naming conventions).

 

 

 

2. 사용 예제

// 기존 코드
Map<String, List<String>> countryToCity = new HashMap<>();
// ...
for (Map.Entry<String, List<String>> citiesInCountry : countryToCity.entrySet()) {
  List<String> cities = citiesInCountry.getValue();
  // ...
}
// var를 사용한 코드
var countryToCity = new HashMap<String, List<String>>();
// ...
for (var citiesInCountry : countryToCity.entrySet()) { // for each문에서 사용 가능
  var cities = citiesInCountry.getValue();
  // ...
}

var를 사용하면 위와 같이 코드 양을 줄일 수 있습니다.

var는 for each문에서도 사용할 수 있으며, C++의 auto와 비슷하게 사용 가능합니다.

 

Object productInfo = new Object() {
        String name = "Apple";
        int total = 30;
};
System.out.println("name = " + productInfo.name + ", total = " + productInfo.total);

익명 클래스를 만드는 경우 필드를 추가할 수 있지만, 다른 곳에서 해당 필드를 참조할 수 없으므로, 위의 코드는 5번 라인에서 에러를 발생시킵니다.

하지만, 위의 코드에서 맨 처음 Object를 var로 바꾸면 익명 클래스에 선언된 필드를 사용할 수 있으므로 에러가 나지 않습니다.

 

 

 

3. 주의해야 할 점

public class VarDemo {
    // var a = 1; // var는 지역변수에서만 사용 가능
    public static void main(String[] args) {
        // var x = 1, y = 2; // ,와 함께 사용 불가능

        // var str = null; // null은 String에서만 사용할 수 있는 것이 아니므로 타입 추론 불가능

        // var err; // var를 사용할 때는 초기화를 바로 해야 함 (타입 추론이 불가능하므로)
        // err = 1;
        
        // var l = () -> {} // 람다식에서 사용 불가능
    }
    // void test(var x) {} // 메소드의 인수타입은 지역변수여도 var 사용 불가
}

위 예제에서 사용한 var는 모두 해당 라인 주석의 이유로 에러를 발생시킵니다.

 

 

 

4. 참고한 사이트

 

JEP 286: Local-Variable Type Inference

JEP 286: Local-Variable Type Inference Summary Enhance the Java Language to extend type inference to declarations of local variables with initializers. Goals We seek to improve the developer experience by reducing the ceremony associated with writing Java

openjdk.java.net

 

Local Variable Type Inference: Frequently Asked Questions

Brian Goetz Stuart Marks 2018-10-11, updated 2019-08-30 Why have var in Java? Local variables are the workhorse of Java. They allow methods to compute significant results by cheaply storing intermediate values. Unlike a field, a local variable is declared,

openjdk.java.net

 

Java 10 Local Variable Type Inference

Java 10 introduced a new shiny language feature called local variable type inference. Sounds fancy! What is it? Let’s work through two situations where Java makes things a little difficult for us as a Java developer. Context: Boilerplate and code readabi

developer.oracle.com

 

'Java' 카테고리의 다른 글

[Java 14] 개선된 switch 문(Enhanced Switch Expressions)  (0) 2020.11.08

+ Recent posts