학습 자료

조건문 — 코드의 길을 나눈다


Article

점수가 90점인지 확인하려다 == 를 하나 빠뜨렸다.

int score = 80;

if (score = 90) {
    System.out.println("만점에 가깝다");
}
터미널
  • error: incompatible types: int cannot be converted to boolean
  • if (score = 90) {
  • ^

컴파일러가 막았다.

score = 90 은 3강에서 봤듯 넣는다는 뜻이다. 넣고 나면 그 결과인 90 이 남는다. if90 을 받아든 셈이다.

어떤 언어들은 여기서 90 을 "0이 아니니까 참"으로 취급하고 조용히 넘어간다. 그리고 조건문은 언제나 참이 되며, score 는 영문도 모른 채 90 으로 바뀐다. 찾기 힘든 버그다.

자바의 ifboolean 만 받는다. 이번 강의는 그 까다로움에서 시작한다.

if 는 boolean 만 받는다

0 도 안 되고 null 도 안 된다. 오직 true 또는 false 다.

int score = 85;

if (score >= 90) {
    System.out.println("A");
} else if (score >= 80) {
    System.out.println("B");
} else {
    System.out.println("C");
}
콘솔
  • B

else if위에서부터 차례로 검사하고, 처음 맞는 곳 하나만 실행하고 빠져나간다.

score95 라면 첫 줄에서 A 를 찍고 끝난다. score >= 80 도 참이지만 쳐다보지 않는다. 그래서 범위를 검사할 때는 좁은 조건을 위에 둔다.

위에서부터 하나만 걸린다
  1. score >= 90 ?참이면 A 를 찍고 끝
  2. score >= 80 ?여기까지 왔다면 90 미만이다
  3. else전부 아니면 여기

순서를 뒤집어 score >= 80 을 위에 두면, 95점도 B 가 된다. 90점 이상은 영원히 도달하지 못한다.

중괄호를 생략하면

if 뒤에 문장이 하나뿐이면 중괄호를 뺄 수 있다.

if (score >= 90)
    System.out.println("A");

되긴 되는데, 이런 일이 생긴다.

if (score >= 90)
    System.out.println("A");
    System.out.println("축하한다");

들여쓰기를 보면 둘 다 조건 안에 있는 것 같다. 하지만 중괄호가 없으면 바로 다음 한 문장까지만 조건에 걸린다. 축하한다 는 점수와 상관없이 언제나 찍힌다.

컴파일러는 들여쓰기를 읽지 않는다. 중괄호는 항상 쓴다. 한 줄이어도 쓴다.

삼항 연산자 — 값을 고르는 if

if 는 문장이라 값을 돌려주지 못한다. 값을 고르고 싶을 때는 물음표를 쓴다.

int score = 85;
String grade = score >= 60 ? "합격" : "불합격";

System.out.println(grade);
콘솔
  • 합격

조건 ? 참일 때 : 거짓일 때 로 읽는다.

짧은 판단에만 쓴다. 삼항 연산자를 두세 개 겹치기 시작하면 그때부터는 if 가 낫다.

&& 와 || 는 게으르다

5강에서 잠깐 언급했던 성질을 이제 써먹는다.

String name = null;

if (name != null && name.length() > 3) {
    System.out.println("긴 이름");
}

namenull 이니 name.length() 를 부르면 4강에서 본 NullPointerException 이 난다. 그런데 이 코드는 멀쩡히 돈다.

&& 는 왼쪽이 false 면 오른쪽을 아예 실행하지 않는다. 결과가 이미 false 로 정해졌기 때문이다.

switch — 값 하나를 여러 갈래로

같은 변수를 여러 값과 비교할 때는 switch 가 읽기 좋다.

옛날 방식부터 본다. 여기에 자바의 유명한 함정이 하나 있다.

int day = 2;

switch (day) {
    case 1: System.out.println("월");
    case 2: System.out.println("화");
    case 3: System.out.println("수");
    default: System.out.println("그 외");
}

day2 니까 만 나와야 할 것 같다.

콘솔
  • 그 외

case 2 를 찾아 들어간 뒤, 거기서 멈추지 않고 아래로 계속 흘러내린다. 이걸 폴스루라고 부른다.

멈추려면 break 를 적어야 한다.

switch (day) {
    case 1: System.out.println("월"); break;
    case 2: System.out.println("화"); break;
    default: System.out.println("그 외");
}

화살표 switch — 폴스루가 없다

자바 14 부터 화살표를 쓸 수 있다. break 가 필요 없고, 흘러내리지도 않는다.

int day = 2;

switch (day) {
    case 1 -> System.out.println("월");
    case 2 -> System.out.println("화");
    default -> System.out.println("그 외");
}
콘솔

한 줄만 실행하고 끝난다. 새 코드에서는 이쪽을 쓴다.

여러 값을 묶으려면 쉼표로 나열한다.

switch (day) {
    case 6, 7 -> System.out.println("주말");
    default   -> System.out.println("평일");
}

switch 가 값을 돌려준다

화살표 switch값을 만들어내는 표현식으로도 쓸 수 있다.

int day = 2;

String name = switch (day) {
    case 1 -> "월";
    case 2 -> "화";
    default -> "?";
};

System.out.println(name);
콘솔

마지막 중괄호 뒤에 세미콜론이 붙는 것에 주의하자. 이건 문장이 아니라 이라서 그렇다. int x = 3; 의 세미콜론과 같은 자리다.

이렇게 쓰면 default 를 빼먹을 수 없다. 값을 돌려줘야 하는데 아무 case 에도 안 걸리면 줄 게 없기 때문이다. 컴파일러가 막아준다.

두 가지 switch

콜론 switch

case 1: … break;

  • break 를 빠뜨리면 아래로 흘러내린다
  • 값을 돌려주지 못한다
  • 옛 코드에서 계속 만난다
  • 읽을 줄은 알아야 한다

화살표 switch

case 1 -> …

  • 흘러내리지 않는다. break 가 없다
  • 값을 돌려줄 수 있다
  • case 6, 7 -> 로 묶을 수 있다
  • 자바 14 이상. 새 코드는 이걸 쓴다

switch 는 문자열을 어떻게 비교하나

switch 에는 문자열도 넣을 수 있다. 그런데 6강에서 문자열은 == 로 비교하면 안 된다고 했다. switch 는 어느 쪽일까.

직접 확인해보자. new String 으로 만들어서 ==false 인 것을 먼저 확인한다.

String s = new String("apple");

System.out.println(s == "apple");

switch (s) {
    case "apple" -> System.out.println("switch 가 잡았다");
    default      -> System.out.println("못 잡았다");
}
콘솔
  • false
  • switch 가 잡았다

== 로는 false 인데 switch 는 잡아냈다. switch 는 내부적으로 .equals() 를 쓴다.

다행이지만, 여기에도 함정이 있다.

String s = null;

switch (s) {
    case "apple" -> System.out.println("사과");
    default      -> System.out.println("그 외");
}
터미널
  • Exception in thread "main" java.lang.NullPointerException

default 로 가주지 않는다. null.equals() 를 부를 대상이 아니기 때문이다. switch 에 넣기 전에 null 을 걸러야 한다.

4강에서 null 이 조용히 흘러 들어온다고 했던 게 이런 뜻이다.

정리하면

자바의 ifboolean 만 받고, switch 는 화살표를 쓰면 값까지 돌려준다.

다시 짚어보기
  1. 01

    if 에는 boolean 만 들어간다

    if (score = 90) 은 컴파일 에러다. 다른 언어의 고전적인 버그를 언어가 막는다.

  2. 02

    중괄호는 언제나 쓴다

    컴파일러는 들여쓰기를 읽지 않는다.

  3. 03

    && 는 왼쪽이 거짓이면 오른쪽을 보지 않는다

    null 검사를 왼쪽에 두면 오른쪽이 보호받는다.

    if (name != null && name.length() > 3) { ... }
  4. 04

    콜론 switch 는 흘러내린다

    break 를 빠뜨리면 아래 case 까지 실행된다. 컴파일러는 잡아주지 않는다.

  5. 05

    화살표 switch 를 쓴다

    흘러내리지 않고, 값을 돌려줄 수 있다.

    String name = switch (day) {
        case 1 -> "월";
        default -> "?";
    };
  6. 06

    switch 는 문자열을 .equals() 로 비교한다

    다만 null 을 넣으면 NullPointerException 이 난다.

이제 길을 나눌 줄 안다. 다음 강의에서는 같은 길을 여러 번 걷는다.

반복문에는 세 가지가 있는데, 자바에는 그중 하나가 좀 특이하게 생겼다. 콜론이 하나 들어간다. 그리고 그걸 쓰려면 9강의 배열이 있어야 한다.