본문 바로가기

switch 식과 삼항 연산

닫힌 선택지를 switch 문·switch 식으로 분리하고 두 값 중 하나를 고르는 경우에만 삼항 연산자를 사용합니다.

if는 범위와 서로 다른 조건식을 다루기 좋지만 하나의 명령 문자열이나 메뉴 번호를 반복 비교하면 대상이 묻힙니다.

switch는 같은 값의 여러 후보를 한곳에 모읍니다.

Java의 전통적인 콜론형 switch 문은 break가 없으면 다음 case로 흐르고, 화살표 switch 식은 분기마다 결괏값을 만들 수 있습니다.

case 1 진입 뒤 break 유무가 실행 범위를 바꾼다

case 1 진입 뒤 break 없음은 네 출력을 계속하고 break는 signup 뒤 탈출합니다. 같은 switch 위치에서 실행 이동·출력을 추적하기 적합합니다.

콜론형 switch의 fall-through와 break 실행 범위 비교

FLOWCHART · FALL-THROUGH

case 1 진입 뒤 break 유무가 실행 범위를 바꾼다

콜론형 switch는 일치한 case에서 진입하지만 다음 case label에서 자동으로 멈추지 않는다. 같은 menu 1도 break 유무에 따라 출력 범위가 달라진다.

case 1 진입 뒤 break 유무가 실행 범위를 바꾼다 동일한 menu 1이 break 없는 콜론형 switch에서는 signup, members, quit, unknown을 차례로 출력하고, case 1의 break가 있으면 signup 뒤 switch 다음 문장으로 이동하는 두 실행 trace다. break 없음 다음 case까지 계속 실행 menu = 1 · case 1 진입 일치 지점 println("signup") 출력 1 case 2 · "members" 출력 2 case 0 · "quit" 출력 3 default · "unknown" 출력 4 switch 뒤로 이동 블록 끝에서 이동 case 1에 break signup만 출력 menu = 1 · case 1 진입 일치 지점 println("signup"); break; signup 출력 후 종료 break switch 뒤로 이동 다른 case는 실행 안 함 case는 진입점을 고르고 break는 switch를 끝냅니다
case 1 진입 뒤 break 유무가 실행 범위를 바꾼다 menu1: break없음 signup→members→quit→unknown→after; break있음 signup→after A · break 없는 menu=1 B · break 있는 menu=1 menu = 1 case 1부터 진입 signup case 1 출력 members case 2 출력 quit case 0 출력 unknown default 출력 after switch switch 다음 실행 menu = 1 case 1부터 진입 signup case 1 출력 after switch break로 switch 종료 뒤 case의 menu는 재비교하지 않음 break 이후 case들은 생략

break 없음 · menu = 1

  1. signup
  2. members
  3. quit
  4. unknown
  5. switch 뒤로 이동

case 1에 break · menu = 1

  1. signup
  2. break로 switch 종료
  3. switch 뒤로 이동

case 2, case 0, default는 실행하지 않습니다.

case label은 진입점을 고르고, break는 가장 가까운 switch의 실행 범위를 끝낸다.

이 예의 조건

  • 두 콜론형 switch는 menu=1이라는 같은 입력입니다.
관계도의 보충 설명
원 도표의 설명과 주석
구분내용
전체 읽기 정보case 1 진입 뒤 break 유무가 실행 범위를 바꾼다 콜론형 switch의 fall-through와 break 실행 범위 비교 FLOWCHART · FALL-THROUGH case 1 진입 뒤 break 유무가 실행 범위를 바꾼다 콜론형 switch는 일치한 case에서 진입하지만 다음 case label에서 자동으로 멈추지 않는다. 같은 menu 1도 break 유무에 따라 출력 범위가 달라진다. break 없음 · menu = 1 signup members quit unknown switch 뒤로 이동 case 1에 break · menu = 1 signup break 로 switch 종료 switch 뒤로 이동 case 2, case 0, default는 실행하지 않습니다. case label은 진입점을 고르고, break 는 가장 가까운 switch의 실행 범위를 끝낸다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
case 1 진입

일치한 case 1부터 signup을 출력합니다.

  • break 없음: signup
  • break 있음: signup 후 break
  • case는 실행의 진입점을 고릅니다.
이후 실행

break 없는 경로는 다음 case 본문을 계속 실행합니다.

  • break 없음: members→quit→unknown
  • break 있음: 다른 case 실행 안 함
  • 다음 case의 조건을 다시 검사하는 흐름이 아닙니다.
switch 이후

두 경로가 switch 다음 문장으로 이동합니다.

  • break 없는 출력: signup,members,quit,unknown
  • break 출력: signup
  • break가 블록 끝 전에 switch를 종료합니다.

case 1 진입 뒤 break 유무가 실행 범위를 바꾼다

case 1 진입 뒤 break 없음은 네 출력을 계속하고 break는 signup 뒤 탈출합니다. 같은 switch 위치에서 실행 이동·출력을 추적하기 적합합니다.

콜론형 switch의 fall-through와 break 실행 범위 비교

FLOWCHART · FALL-THROUGH

case 1 진입 뒤 break 유무가 실행 범위를 바꾼다

콜론형 switch는 일치한 case에서 진입하지만 다음 case label에서 자동으로 멈추지 않는다. 같은 menu 1도 break 유무에 따라 출력 범위가 달라진다.

case 1 진입 뒤 break 유무가 실행 범위를 바꾼다 동일한 menu 1이 break 없는 콜론형 switch에서는 signup, members, quit, unknown을 차례로 출력하고, case 1의 break가 있으면 signup 뒤 switch 다음 문장으로 이동하는 두 실행 trace다. break 없음 다음 case까지 계속 실행 menu = 1 · case 1 진입 일치 지점 println("signup") 출력 1 case 2 · "members" 출력 2 case 0 · "quit" 출력 3 default · "unknown" 출력 4 switch 뒤로 이동 블록 끝에서 이동 case 1에 break signup만 출력 menu = 1 · case 1 진입 일치 지점 println("signup"); break; signup 출력 후 종료 break switch 뒤로 이동 다른 case는 실행 안 함 case는 진입점을 고르고 break는 switch를 끝냅니다
case 1 진입 뒤 break 유무가 실행 범위를 바꾼다 menu1: break없음 signup→members→quit→unknown→after; break있음 signup→after A · break 없는 menu=1 B · break 있는 menu=1 menu = 1 case 1부터 진입 signup case 1 출력 members case 2 출력 quit case 0 출력 unknown default 출력 after switch switch 다음 실행 menu = 1 case 1부터 진입 signup case 1 출력 after switch break로 switch 종료 뒤 case의 menu는 재비교하지 않음 break 이후 case들은 생략

break 없음 · menu = 1

  1. signup
  2. members
  3. quit
  4. unknown
  5. switch 뒤로 이동

case 1에 break · menu = 1

  1. signup
  2. break로 switch 종료
  3. switch 뒤로 이동

case 2, case 0, default는 실행하지 않습니다.

case label은 진입점을 고르고, break는 가장 가까운 switch의 실행 범위를 끝낸다.

이 예의 조건

  • 두 콜론형 switch는 menu=1이라는 같은 입력입니다.

관계도의 보충 설명

원 도표의 설명과 주석
구분내용
전체 읽기 정보case 1 진입 뒤 break 유무가 실행 범위를 바꾼다 콜론형 switch의 fall-through와 break 실행 범위 비교 FLOWCHART · FALL-THROUGH case 1 진입 뒤 break 유무가 실행 범위를 바꾼다 콜론형 switch는 일치한 case에서 진입하지만 다음 case label에서 자동으로 멈추지 않는다. 같은 menu 1도 break 유무에 따라 출력 범위가 달라진다. break 없음 · menu = 1 signup members quit unknown switch 뒤로 이동 case 1에 break · menu = 1 signup break 로 switch 종료 switch 뒤로 이동 case 2, case 0, default는 실행하지 않습니다. case label은 진입점을 고르고, break 는 가장 가까운 switch의 실행 범위를 끝낸다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
case 1 진입

일치한 case 1부터 signup을 출력합니다.

  • break 없음: signup
  • break 있음: signup 후 break
  • case는 실행의 진입점을 고릅니다.
이후 실행

break 없는 경로는 다음 case 본문을 계속 실행합니다.

  • break 없음: members→quit→unknown
  • break 있음: 다른 case 실행 안 함
  • 다음 case의 조건을 다시 검사하는 흐름이 아닙니다.
switch 이후

두 경로가 switch 다음 문장으로 이동합니다.

  • break 없는 출력: signup,members,quit,unknown
  • break 출력: signup
  • break가 블록 끝 전에 switch를 종료합니다.

switch 관통 실행

lab/SwitchFallThrough.java
public final class SwitchFallThrough {
    public static void main(String[] args) {
        int menu = 1;

        switch (menu) {
            case 1:
                System.out.println("signup");
            case 2:
                System.out.println("members");
            case 0:
                System.out.println("quit");
            default:
                System.out.println("unknown");
        }
    }
}
signup
members
quit
unknown

case 1에서 시작한 뒤 switch 블록 끝까지 순서대로 실행됐습니다.

case는 다음 case에 도착했다고 자동으로 멈추지 않습니다.

의도적인 fall-through로 여러 후보가 같은 코드를 공유할 수도 있지만, 누락인지 의도인지 구분하기 어렵습니다.

콜론형 문을 유지한다면 각 경로에서 break로 switch를 빠져나옵니다.

src/SwitchStatement.java
public final class SwitchStatement {
    public static void main(String[] args) {
        int menu = 1;

        switch (menu) {
            case 1:
                System.out.println("signup");
                break;
            case 2:
                System.out.println("members");
                break;
            case 0:
                System.out.println("quit");
                break;
            default:
                System.out.println("unknown");
        }
    }
}

break는 가장 가까운 switch 문을 끝내고 그다음 문장으로 이동합니다.

반복문의 break와 이름은 같지만 빠져나가는 대상은 현재 감싸고 있는 switch입니다.


switch 표현식

src/CommandLabel.java
public final class CommandLabel {
    public static void main(String[] args) {
        String command = args.length == 0 ? "help" : args[0];

        String label = switch (command) {
            case "env" -> "toolchain report";
            case "runtime" -> "runtime report";
            case "paths" -> "workspace report";
            case "signup" -> "register a member";
            case "help" -> "show usage";
            default -> "unknown command";
        };

        System.out.println(label);
    }
}
java -cp out CommandLabel signup
register a member

switch (command) 전체가 String 값을 만들고 마지막 세미콜론에서 대입 문장이 끝납니다.

화살표 오른쪽 분기는 다음 case로 흘러가지 않습니다.

label을 먼저 빈 값으로 초기화한 뒤 각 case에서 다시 대입할 필요도 없습니다.

여러 문장을 실행한 뒤 값을 내놓으려면 블록과 yield를 사용합니다.

src/SwitchYield.java
public final class SwitchYield {
    public static void main(String[] args) {
        String role = "member";

        String description = switch (role) {
            case "guest" -> "read only";
            case "member" -> {
                System.out.println("role=" + role);
                yield "write posts";
            }
            case "admin" -> "manage members";
            default -> "unknown role";
        };

        System.out.println(description);
    }
}
role=member
write posts

yield는 switch 식의 해당 분기 결과를 제공합니다.

메서드를 끝내는 return과 다릅니다.

switch 식에서 정상 완료하는 모든 분기는 호환되는 타입의 결과를 제공해야 하며, 값을 만들지 않는 분기는 예외로 끝나야 합니다.

switch expression의 command case와 String 결과 값 매핑

TREE · SWITCH VALUE MAP

각 case는 String 값 하나를 골라 label을 초기화한다

command 값을 평가한 뒤 해당 case의 String 결과 하나로 label을 초기화합니다. 화살표 case는 다음 case로 흐르지 않습니다.

각 case는 String 값 하나를 골라 label을 초기화한다 env/runtime/paths/signup/help/default중한 case의String값만label에대입; 후보간순차실행없음 command 문자열 택1 · 한 case만 실행 env case 결과값 toolchain report runtime case 결과값 runtime report paths case 결과값 workspace report signup case 결과값 register a member help case 결과값 show usage 그 외 · default case 결과값 unknown command String label 선택된 하나의 값 대입
각 case는 String 값 하나를 골라 label을 초기화한다 env/runtime/paths/signup/help/default중한 case의String값만label에대입; 후보간순차실행없음 command 문자열 여섯 후보 중 하나만 선택 env 선택된 case의 값 toolchain report runtime 선택된 case의 값 runtime report paths 선택된 case의 값 workspace report signup 선택된 case의 값 register a member help 선택된 case의 값 show usage 그 외 · default 선택된 case의 값 unknown command String label 선택된 하나의 결과값 대입 각 행은 후보 · 실행 순서 아님

command → 일치하는 case → String label

이 예제에서는 아래 결과 중 하나를 선택합니다.

CommandLabel의 입력과 결과 값
case 입력선택되는 String 값
"env""toolchain report"
"runtime""runtime report"
"paths""workspace report"
"signup""register a member"
"help""show usage"
default"unknown command"
"env"
"toolchain report"
"runtime"
"runtime report"
"paths"
"workspace report"
"signup"
"register a member"
"help"
"show usage"
default
"unknown command"

여러 문장이 필요한 분기는 yield로 값을 제공합니다

println("role=" + role);

yield "write posts";

본문의 별도 SwitchYield 예제에서는 role이 member일 때 메시지를 먼저 출력하고, yield가 String 결과를 제공합니다. yield는 메서드를 끝내는 return과 다릅니다.

화살표 case는 다음 case로 흐르지 않으며 선택된 값 하나만 label 초기화에 사용된다.


범위 조건과 if

switch의 후보는 같은 선택 값에 대한 닫힌 목록일 때 가장 잘 읽힙니다.

age < 18, age <= 120처럼 범위를 순서대로 제거하는 조건은 if/else if가 직접적입니다.

반대로 command.equals("env"), command.equals("runtime")을 반복하는 코드는 switch가 비교 대상을 한 번만 보여 줍니다.

문제우선할 표현이유
1–120세 범위 검증if서로 다른 비교식과 순서가 핵심
명령 문자열 선택switch 식같은 값의 후보 목록
메뉴별 여러 문장 실행화살표 switch 문fall-through 없이 블록 실행
두 라벨 중 하나 선택삼항 연산자결과가 정확히 두 값

삼항 연산자의 범위

src/TernaryLabel.java
public final class TernaryLabel {
    public static void main(String[] args) {
        int age = 20;
        boolean adult = age >= 18;

        String status = adult ? "adult" : "underage";
        System.out.println(status);
    }
}

조건이 참이면 ?와 : 사이 값을, 거짓이면 : 뒤 값을 선택합니다.

삼항 연산자도 값 하나를 만드는 식이므로 변수 초기화나 메서드 인자에 사용할 수 있습니다.

세 단계 이상을 중첩하면 조건과 결과의 짝이 빠르게 흐려집니다.

String label = age < 18 ? "underage"
    : age < 65 ? "adult"
    : "senior";

문법상 동작하지만 분기 순서와 범위를 검토하기는 if/else if가 쉽습니다.

“참과 거짓에 각각 짧은 값 하나”라는 형태를 벗어나면 조건문이나 switch 식으로 풀어냅니다.

출력이나 상태 변경을 삼항 연산자의 두 분기에 넣지 않습니다.


명령 라우팅과 상태 라벨

CLI의 바깥 switch는 어떤 기능을 호출할지 고르고, register 안의 if는 입력 범위를 검증하며, 삼항 연산자는 성인 여부 라벨 두 개 중 하나를 고릅니다.

서로 다른 판단을 한 표현에 몰지 않습니다.

이 예제는 register 뒤에 정수 모양의 나이가 들어온다고 가정하며, 숫자 형식이나 int 범위 초과로 발생하는 NumberFormatException 복구는 ch3에서 다룹니다.

src/SignupRouting.java
public final class SignupRouting {
    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("command required");
            return;
        }

        switch (args[0]) {
            case "register" -> register(args);
            case "help" -> System.out.println("register <email> <age>");
            default -> System.out.println("unknown command: " + args[0]);
        }
    }

    private static void register(String[] args) {
        if (args.length != 3) {
            System.out.println("register requires email and age");
            return;
        }

        String email = args[1];
        int age = Integer.parseInt(args[2]);
        if (email.isBlank() || age < 1 || age > 120) {
            System.out.println("invalid member");
            return;
        }

        String status = age >= 18 ? "adult" : "underage";
        System.out.println(email + ":" + age + ":" + status);
    }
}

register(args)는 아래에 정의한 register 기능을 호출하면서 같은 인자 배열을 전달합니다. main이 고른 명령도 배열에 남아 있으므로 args[0]은 register, 이메일은 args[1], 나이는 args[2]입니다. 메서드를 만드는 문법은 다음 장에서 다루며, 여기서는 호출했다가 돌아오는 순서만 확인합니다.

예시

register [email protected] 20은 명령 선택 → register 호출 → 인자 수 3 확인 → 이메일·나이 읽기 → 범위 검사 → adult 선택 → 출력 순서로 진행합니다. register 안의 return은 그 기능을 끝내고 호출한 main으로 돌아갑니다. 이 프로그램의 main은 switch 뒤에 할 일이 없어 곧 종료됩니다.

대표 실행
> java -cp out SignupRouting register [email protected] 20
[email protected]:20:adult

> java -cp out SignupRouting help
register <email> <age>

연습 문제

FormatSelector를 작성하세요.

첫 인자는 plain, compact, debug 중 하나이며 switch 식으로 라벨을 만듭니다.

둘째 인자 나이가 18세 이상이면 삼항 연산자로 adult, 아니면 underage를 선택합니다.

알 수 없는 형식은 형식 라벨을 unsupported로 정하고 나이 라벨과 함께 출력합니다.

해설 보기
src/FormatSelector.java
public final class FormatSelector {
    public static void main(String[] args) {
        String format = args[0];
        int age = Integer.parseInt(args[1]);

        String formatLabel = switch (format) {
            case "plain" -> "text";
            case "compact" -> "one-line";
            case "debug" -> "diagnostic";
            default -> "unsupported";
        };
        String ageLabel = age >= 18 ? "adult" : "underage";

        System.out.println(formatLabel + ":" + ageLabel);
    }
}

java -cp out FormatSelector debug 20은 diagnostic:adult를 출력합니다.

형식 후보는 switch에, 연속적인 나이 범위의 두 결과는 비교와 삼항 연산자에 남아 판단의 종류가 분리됩니다.