KB 09 when — break 없는 switch
고친 사람 github-actions[bot]
0. break 를 빼먹은 switch, break 가 없는 when
자바의 옛 switch 에서 break 를 빼먹어 본 적이 있다면 결과를 압니다. 코틀린의 when 에는 그 break 를 쓸 곳이 아예 없습니다. 같은 모양으로 둘을 맞대 봤습니다.
fun main() {
val x = 1
when (x) {
1 -> println("one") // one
2 -> println("two")
else -> println("other")
}
}
fun main()— 프로그램이 시작하는 함수입니다. 클래스 없이 파일 바로 아래에 둡니다val x = 1— 다시 대입할 수 없는 변수입니다. 타입을 안 적으면 값에서Int를 알아냅니다when (x) { ... }— 괄호 안 값을 아래 줄들과 위에서부터 차례로 견줘, 처음 맞는 줄 하나를 실행합니다1 -> ...— 이런 줄 하나를 분기라고 부릅니다. 화살표 왼쪽이 분기 조건, 오른쪽이 실행할 코드입니다.else는 자바의default에 해당합니다
First.kt 로 저장하고 코틀린 컴파일러 kotlinc 로 컴파일해 돌렸습니다.
$ kotlinc First.kt -d out
$ kotlin -cp out FirstKt
-d out— 결과물을out폴더에 쓰라는 옵션입니다-cp out— 클래스를 찾을 폴더(classpath)를out으로 정합니다kotlin— 코틀린 설치에 딸린 실행 명령입니다.java와 같은 일을 하되, 코틀린 표준 라이브러리를 classpath 에 스스로 넣습니다FirstKt— 컴파일러가 파일 이름First에Kt를 붙여 지은 클래스입니다. 파일 바로 아래 쓴 함수는 이 클래스의 static 메서드가 됩니다
같은 모양의 자바 switch 입니다. case 마다 break 를 쓰지 않고 jout 폴더에 컴파일해 돌렸습니다.
public class First {
public static void main(String[] args) {
int x = 1;
switch (x) {
case 1:
System.out.println("one");
case 2:
System.out.println("two");
default:
System.out.println("other");
}
}
}
$ javac -d jout First.java
$ java -cp jout First
one
two
other
자바는 세 줄, 코틀린은 한 줄입니다. 자바의 case 1: 은 들어가는 입구일 뿐이라, break 가 없으면 아래 case 의 코드까지 이어서 실행합니다. 이 동작을 fall-through 라고 부릅니다. 코틀린의 when 은 맞은 분기 하나만 실행하고 끝납니다. 다음 분기로 넘어가는 문법이 없으니 fall-through 도 없습니다.
자바도 JDK 14 에서 화살표 모양의 case 1 -> 를 정식 문법으로 들였습니다. 이 모양 역시 fall-through 가 없습니다(JEP 361. JEP 는 JDK 에 들어갈 기능을 적은 제안 문서입니다). 겉모습만 보면 when 은 이 화살표 switch 와 닮았습니다. 둘은 화살표 왼쪽에 무엇을 쓸 수 있느냐에서 갈리는데, 1절부터 4절까지가 그 이야기입니다.
이 편의 출력은 전부 Kotlin 2.4.10, JDK 21 에서 직접 돌린 결과입니다.
when 안의 break 는 반복문을 빠져나간다
그럼 자바 버릇대로 when 안에 break 를 쓰면 어떻게 될까요. 컴파일 에러가 아니라 다른 동작이 나옵니다.
fun main() {
for (i in 1..3) {
when (i) {
2 -> break
else -> println(i) // 1
}
}
println("end") // end
}
for (i in 1..3)—i를 1, 2, 3 으로 바꿔 가며 도는 반복입니다.1..3은 1 이상 3 이하의 범위입니다. 범위 문법은 KB 10 범위와 반복 편에서 자세히 봅니다2 -> break—i가 2 일 때break를 실행합니다
코틀린 쪽을 돌렸습니다.
$ kotlinc Loop.kt -d out
$ kotlin -cp out LoopKt
같은 일을 하는 자바 Loop.java 는 for (int i = 1; i <= 3; i++) 안에 이런 switch 를 두었고, 컴파일해 돌리니 세 줄이 나왔습니다.
switch (i) {
case 2: break;
default: System.out.println(i);
}
$ javac -d jout Loop.java
$ java -cp jout Loop
1
3
end
코틀린은 3 을 찍지 않았습니다. 자바의 break 는 가장 가까운 switch 를 빠져나가므로 반복이 계속됩니다. 코틀린의 when 은 break 로 빠져나가는 대상이 아닙니다. 그래서 break 가 바깥 for 를 끝냅니다(코틀린 1.4 부터 이렇게 동작합니다. 그 전에는 컴파일 에러였습니다).
1. 값 매칭 — 여러 값, 식, else
when 을 가장 흔히 쓰는 모양은 괄호 안 값을 후보 여럿과 견주는 것입니다. 이 절에서는 분기 조건에 값을 몇 개, 어떤 모양으로 쓸 수 있는지를 한 함수로 봅니다.
fun describe(x: Int, limit: Int) {
when (x) {
0 -> println("zero")
1, 2 -> println("small")
limit -> println("limit")
limit * 2 -> println("double")
else -> println("other")
}
}
fun main() {
describe(2, 10) // small
describe(10, 10) // limit
describe(20, 10) // double
describe(7, 10) // other
}
x: Int— 코틀린은 이름 뒤에 콜론, 그 뒤에 타입을 씁니다. 자바의int x입니다1, 2 ->— 쉼표로 늘어놓은 값 중 하나라도 같으면 이 분기를 실행합니다limit ->— 상수가 아니라 매개변수입니다.x가limit과 같으면 맞습니다limit * 2 ->— 계산식도 분기 조건이 됩니다
$ kotlinc Values.kt -d out
$ kotlin -cp out ValuesKt
2 는 1, 2 분기에, 10 은 limit 분기에, 20 은 limit * 2 분기에 맞았습니다. 어디에도 안 맞은 7 은 else 로 갔습니다.
같은 함수를 JDK 21 의 화살표 switch 로 옮긴 Values.java 입니다. case 1, 2 -> 줄에서는 에러가 없었고, case limit -> 줄에서 멈췄습니다. limit * 2 도 상수가 아니라서 자바 쪽에는 처음부터 넣지 않았습니다.
$ javac -d jout Values.java
Values.java:6: error: constant expression required
case limit -> System.out.println("limit");
^
1 error
constant expression required 는 이 case 에 컴파일 시점 상수가 와야 한다는 뜻입니다. 컴파일 시점 상수는 컴파일할 때 이미 값이 정해져 있는 것, 곧 1 같은 리터럴이나 final 상수입니다. limit 은 부를 때마다 값이 달라지는 매개변수라 여기 올 수 없습니다. 자바의 case 는 상수만 받습니다.
코틀린은 견주는 방식부터 다릅니다. 괄호 안 값이 분기 조건 식의 값과 같은지를 위에서부터 차례로 비교할 뿐입니다. 그래서 괄호 안 값과 비교할 수 있는 타입이면 어떤 식이든 분기 조건이 됩니다.
다만 타입은 맞아야 합니다. Int 값에 "a" -> 를 쓴 IntStr.kt 는 error: incompatible types 'Int' and 'String'. 로 멈췄습니다.
같은 값이 두 번 나오면 코틀린은 경고만 냅니다. 1 -> 를 두 번 쓴 Dup.kt 에서 warning: duplicate branch condition in 'when'. 이 나왔습니다. 실행하면 첫 분기의 first 만 찍었습니다. 자바는 같은 모양을 error: duplicate case label 로 거절했습니다.
when 의 분기 조건은 상수가 아니어도 됩니다. 매개변수든 계산식이든, 괄호 안 값과 견줄 수만 있으면 놓을 수 있습니다.
2. 인자 없는 when — if-else if 사슬 대신
1절의 when 은 값 하나를 여러 후보와 견줬습니다. 그런데 조건마다 보는 변수가 다르면 견줄 값 하나가 없습니다. 이럴 때 코틀린은 괄호를 떼고 조건만 늘어놓습니다. 같은 조건을 자바의 if-else if 사슬로 옮긴 Grade.java 와 함께 돌렸습니다.
fun main() {
val score = 85
val name = ""
when {
score >= 90 -> println("A")
// no name
name.isEmpty() -> println("no name")
score >= 80 -> println("B")
else -> println("C")
}
}
$ kotlinc Grade.kt -d out
$ kotlin -cp out GradeKt
$ javac -d jout Grade.java
$ java -cp jout Grade
when {— 괄호 안 값이 없는 모양입니다. 분기 조건마다 참이나 거짓이 나오는 식을 씁니다name.isEmpty()— 빈 문자열이면 참입니다. 앞뒤 분기와 달리score가 아니라name을 봅니다
코틀린과 자바 모두 no name 한 줄을 찍었습니다. 둘 다 B 가 아니라 no name 입니다. score >= 80 도 참이지만, 그보다 위의 name.isEmpty() 가 먼저 참이 되어 거기서 끝났습니다. 인자 없는 when 은 if-else if 사슬과 같은 순서로 판정합니다.
대신 분기 조건이 참이나 거짓, 곧 Boolean 이어야 합니다. Boolean 은 자바의 boolean 에 해당하는 코틀린 타입입니다. 인자 없는 when 에 score -> 를 쓰면 kotlinc 가 error: condition type mismatch: inferred type is 'Int' but 'Boolean' was expected. 로 멈춥니다.
3. is 매칭과 스마트 캐스트
타입에 따라 갈라지는 코드는 자바에서 instanceof 사슬로 써 왔습니다. JDK 21 부터는 switch 의 case 에 타입 패턴을 쓸 수 있습니다(JEP 441). 이 절에서는 같은 함수를 코틀린의 is(타입 검사) 분기와 JDK 21 패턴 switch 로 써서 맞댑니다.
fun describe(x: Any) {
when (x) {
is String -> println("String, length " + x.length)
is Int -> println("Int, plus one " + (x + 1))
else -> println("something else")
}
}
fun main() {
describe("kotlin") // String, length 6
describe(41) // Int, plus one 42
describe(2.5) // something else
}
x: Any—Any는 모든 코틀린 타입의 부모 타입입니다. 자바에서Object로 받던 매개변수에 씁니다is String—x가String이면 맞습니다. 자바의instanceof입니다x.length— 문자열 길이입니다.x는Any인데 캐스트 없이 불렀습니다x + 1—Int분기에서도 캐스트 없이x에 1 을 더했습니다
같은 일을 JDK 21 패턴 switch 로 쓴 자바입니다. main 은 코틀린과 같은 세 번의 호출이라 ... 로 줄였습니다.
public class Is {
static void describe(Object x) {
switch (x) {
case String s ->
System.out.println("String, length " + s.length());
case Integer i ->
System.out.println("Int, plus one " + (i + 1));
default ->
System.out.println("something else");
}
}
...
}
코틀린 쪽은 이렇게 돌렸습니다.
$ kotlinc Is.kt -d out
$ kotlin -cp out IsKt
자바 Is.java 도 같은 세 줄을 찍었습니다. 여기서 볼 것은 분기 안에서 무엇으로 값을 쓰느냐 하나입니다.
자바는 case String s 로 새 변수 s 를 만들고, 분기 안에서 x 대신 s 를 씁니다. 코틀린은 새 변수 없이 x 를 그대로 씁니다. is String 이 맞은 분기 안에서는 컴파일러가 x 를 String 으로 다루기 때문입니다. 이렇게 타입 검사를 통과한 변수를 컴파일러가 그 타입으로 좁혀 주는 기능을 스마트 캐스트(smart cast)라고 부릅니다.
스마트 캐스트는 검사와 사용 사이에 값이 바뀔 수 없는 변수에서만 됩니다. 위의 x 는 매개변수라 바뀌지 않습니다.
바뀔 수 있는 쪽은 막힙니다. 클래스에 딸린 var(다시 대입할 수 있는 변수) v 를 when 으로 검사하고 그 분기 안에서 length 를 부른 VarProp.kt 는 컴파일되지 않았습니다. 에러는 error: smart cast to 'String' is impossible, because 'v' is a mutable property that could be mutated concurrently. 입니다. 검사한 뒤 다른 코드가 v 를 바꿀 수 있다는 뜻입니다.
그럼 캐스트는 누가 쓸까요. 컴파일러가 대신 써 넣습니다. 컴파일 결과인 .class 파일을 자바 코드로 되읽어 주는 디컴파일러 CFR 로 그 자리를 확인했습니다.
$ java -jar cfr.jar out/IsKt.class
...
public final class IsKt {
public static final void describe(@NotNull Object x) {
Intrinsics.checkNotNullParameter((Object)x, (String)"x");
Object object = x;
if (object instanceof String) {
System.out.println((Object)("String, length " + ((String)x).length()));
} else if (object instanceof Integer) {
System.out.println((Object)("Int, plus one " + (((Number)x).intValue() + 1)));
} else {
System.out.println((Object)"something else");
}
}
...
instanceof사슬 —is분기가if-else if와instanceof로 바뀌었습니다((String)x).length()— 소스에 없던 캐스트입니다. 스마트 캐스트는 컴파일러가 캐스트를 대신 써 넣는 것입니다((Number)x).intValue()—Any에 담긴 정수는Integer객체라, 캐스트한 뒤int값을 꺼냅니다.Number는Integer의 부모 클래스입니다Intrinsics.checkNotNullParameter— 인자가null이면 멈추는 검사입니다.Any는null을 받지 않는 타입이라 붙었습니다@NotNull— 매개변수나 반환값이null이 아니라는 표시로 컴파일러가 붙인 애노테이션입니다
CFR 출력은 이렇게 줄여 실었습니다. 맨 위의 머리 주석, import, 한 줄이 아주 긴 @Metadata(...)(코틀린 컴파일러가 남기는 부가 정보)를 ... 로 걷었습니다. 디컴파일 결과는 원래 소스를 되살린 것이 아니라 클래스 파일에 적힌 내용을 자바 문법으로 다시 적은 것입니다.
같은 일을 자바는 다른 길로 합니다. 자바 쪽 Is.class 를 CFR 로 열면 instanceof 사슬이 없습니다. java.lang.runtime.SwitchBootstraps.typeSwitch 라는 JDK 메서드에 String.class, Integer.class 목록을 넘겨 몇 번째 case 가 맞는지 받아 온 뒤, 그 번호로 switch 를 가릅니다.
분기 순서 규칙은 두 언어가 갈립니다. String 의 부모 타입인 is CharSequence 를 is String 보다 위에 둔 코틀린 Order.kt 는 경고 없이 컴파일됐고, "kotlin" 을 넣으면 CharSequence 를 찍었습니다. 같은 순서의 자바는 error: this case label is dominated by a preceding case label 로 거절했습니다. 앞 case 가 뒤 case 를 가려 뒤는 영영 실행될 수 없다는 뜻입니다.
정리하면, 코틀린의 is 분기는 instanceof 사슬로 컴파일됩니다. 분기 안의 캐스트는 컴파일러가 써 넣습니다. 앞 분기에 가려진 분기를 자바는 에러로 막지만, 코틀린은 통과시킵니다.
4. in 범위 매칭
나이·점수·코드 번호처럼 값이 어느 구간에 드느냐로 가르는 일도 흔합니다. 자바 switch 는 구간을 case 로 못 쓰니 보통 if 사슬로 갑니다. 코틀린은 분기 조건에 in 과 범위를 씁니다.
fun describe(age: Int) {
when (age) {
in 0..12 -> println("child")
in 13..19 -> println("teen")
!in 0..150 -> println("invalid")
else -> println("adult")
}
}
fun main() {
describe(15) // teen
describe(200) // invalid
describe(40) // adult
}
in 0..12—age가 0 이상 12 이하면 맞습니다...는 양 끝을 포함하는 범위를 만듭니다!in 0..150— 범위 밖이면 맞습니다- 범위를 만드는 다른 방법과 범위 객체의 속은 KB 10 범위와 반복 편에서 봅니다
$ kotlinc Age.kt -d out
$ kotlin -cp out AgeKt
15 는 13..19 에, 200 은 !in 0..150 에 맞았습니다. 40 은 네 분기 중 어디에도 안 맞아 else 로 갔습니다.
자바 쪽에는 구간을 적는 case 가 없습니다. 대신 JDK 21 패턴 switch 에서 case 뒤에 조건을 더 붙이는 when 절을 씁니다. 코틀린의 when 과 이름만 같은 자바 키워드입니다.
이걸로 옮긴 Age.java 는 case Integer a when a >= 13 && a <= 19 -> 처럼 구간마다 조건을 손으로 붙였습니다. int age 를 받은 채로는 error: incompatible types: pattern of type Integer is not applicable at int 로 멈췄고, 매개변수를 Integer age 로 바꾼 Age2.java 는 컴파일돼 코틀린과 같은 teen·invalid·adult 를 찍었습니다.
그래도 자바 쪽은 구간마다 a >= 13 && a <= 19 처럼 양 끝 비교를 손으로 씁니다. 코틀린은 in 13..19 한 마디입니다.
5. 식으로 쓰는 when 과 else
지금까지는 분기마다 println 을 불렀습니다. when 은 값을 돌려주는 식으로도 쓸 수 있습니다. 이 절에서는 식으로 쓸 때 else 가 왜 필요한지, enum 이면 왜 빼도 되는지, 컴파일이 끝난 뒤 상수가 늘면 어떻게 되는지, 문으로 쓰면 무엇이 달라지는지를 에러 원문과 실행으로 봅니다.
Int 값에 else 가 없으면
fun name(x: Int): String {
val s = when (x) {
1 -> "one"
2 -> "two"
}
return s
}
fun name(x: Int): String— 괄호 뒤: String이 반환 타입입니다val s = when (x) { ... }— 맞은 분기의 화살표 오른쪽 값이when전체의 값이 되어s에 들어갑니다. 자바의switch식에 해당합니다
$ kotlinc Expr.kt -d out
Expr.kt:2:13: error: 'when' expression must be exhaustive. Add an 'else' branch.
val s = when (x) {
^^^^
when 식은 모든 경우를 빠짐없이 다뤄야 하니 else 분기를 더하라는 에러입니다. x 가 3 이면 s 에 넣을 값이 없기 때문입니다. Int 가 담을 수 있는 값은 약 43억 가지입니다. 분기 조건 몇 개로 그 전부를 짚을 수는 없으니, 나머지를 받을 else 가 있어야 합니다.
같은 모양의 자바 switch 식(String s = switch (x) { case 1 -> "one"; case 2 -> "two"; };)도 error: the switch expression does not cover all possible input values 로 거절됩니다.
enum 이면 else 를 뺄 수 있다
값의 가짓수를 컴파일러가 전부 아는 타입이면 이야기가 달라집니다. 코틀린의 enum class 는 자바의 enum 과 같은 열거형입니다.
// Color.kt
enum class Color { RED, GREEN, BLUE }
// Paint.kt
fun label(c: Color): String {
return when (c) {
Color.RED -> "red"
Color.GREEN -> "green"
Color.BLUE -> "blue"
}
}
enum class Color { RED, GREEN, BLUE }— 상수 셋을 가진 열거형입니다
kotlinc Color.kt Paint.kt -d out 은 else 가 없는데도 에러 없이 끝났습니다. 상수 셋을 모두 분기로 짚었기 때문입니다. BLUE 분기를 지운 Missing.kt 로 바꿔 컴파일했습니다.
$ kotlinc Color.kt Missing.kt -d out
Missing.kt:2:12: error: 'when' expression must be exhaustive. Add the 'BLUE' branch or an 'else' branch.
return when (c) {
^^^^
에러가 빠진 상수 이름 BLUE 를 짚었습니다. Int 와 달리 Color 의 값은 RED·GREEN·BLUE 셋뿐이라, 컴파일러가 무엇이 빠졌는지 셀 수 있습니다.
자바도 같습니다. 상수 셋을 모두 case 로 짚은 switch 식은 default 없이 컴파일됐습니다. 하나를 빼면 Int 때와 같은 the switch expression does not cover all possible input values 에러로 멈췄습니다.
없던 상수가 실행 시점에 오면
빠짐없이 짚었는지 세는 이 검사는 컴파일할 때 하는 것입니다. 그런데 Color.kt 와 Paint.kt 는 따로 컴파일할 수 있습니다. label 을 컴파일한 뒤에 상수가 늘어나면 어떻게 되는지 해 봤습니다.
Color 에 YELLOW 를 더해 Color.kt 만 다시 컴파일했습니다. 그다음 Color.valueOf("YELLOW") 로 만든 값을 옛 PaintKt.class 의 label 에 넘겼습니다. 결과는 kotlin.NoWhenBranchMatchedException 이었습니다. label 은 RED·GREEN·BLUE 셋만 짚은 채로 컴파일돼 있어서, 그 셋 중 어디에도 맞지 않는 값이 오면 이 예외가 납니다.
같은 실험에서 자바는 java.lang.MatchException 을 던졌습니다.
else 를 뺀 enum when 은 컴파일할 때 빠짐없이 짚었는지 검사한 것입니다. 실행 시점에 모르는 상수가 오면 예외로 끝납니다.
문으로 쓰면 타입에 따라 갈린다
println 만 부르는 문으로 쓸 때는 돌려줄 값이 없습니다. 그래도 else 를 빼도 되는지는 괄호 안 값의 타입에 따라 갈립니다.
Any·Int 처럼 가짓수가 열린 타입이면, 문에서 어느 분기에도 안 맞는 값은 그냥 지나갑니다. 3절처럼 is String·is Int 분기만 두고 else 를 뺀 NoElse.kt 에 2.5 를 넘기고, 그다음 줄에 println("end") 를 두었습니다.
$ kotlinc NoElse.kt -d out
$ kotlin -cp out NoElseKt
end
컴파일도 실행도 막히지 않았고, 분기에서는 아무것도 찍히지 않았습니다. 1 -> 분기 하나만 둔 Int 문 IntStmt.kt 에 7 을 넣었을 때도 end 만 나왔습니다.
자바는 여기서 다릅니다. default 를 뺀 패턴 switch 문 NoDefault.java 는 error: the switch statement does not cover all possible input values 로 거절됐습니다. JDK 21 은 패턴을 쓴 switch 라면 문이어도 모든 값을 짚게 합니다.
괄호 안 값이 enum·Boolean·sealed 클래스처럼 가짓수가 정해진 타입이면, 코틀린도 문에서 빠짐없이 짚어야 합니다. sealed 클래스는 자식 클래스 목록을 컴파일러가 전부 아는 클래스입니다. 이런 타입은 빠진 경우를 컴파일러가 셀 수 있어서, 코틀린 1.7 부터 문에서 빠뜨려도 에러를 냅니다.
Color 를 받아 RED·GREEN 두 분기만 println 으로 짚은 문 Stmt.kt 는 Add the 'BLUE' branch or an 'else' branch. 에러로 멈췄습니다. true 분기 하나만 둔 Boolean 문 BoolStmt.kt 도 Add the 'false' branch or an 'else' branch. 에러로 멈췄습니다.
정리하면, when 을 식으로 쓰면 모든 값에 결과가 있어야 해서 else 가 필요합니다. enum 과 Boolean 처럼 가짓수가 정해진 타입은 전부 짚으면 else 를 뺄 수 있습니다. Boolean 값을 true·false 두 분기로 가른 when 식 Bool.kt 는 else 없이 통과했습니다. 이런 타입은 문으로 써도 빠짐없이 짚어야 합니다.
6. 한 장 요약
when은 맞은 분기 하나만 실행하는 switch 라 break 도 fall-through 도 없고, 분기 조건에 여러 값·변수·식·is타입·in범위를 쓴다. 식으로 쓰면 모든 값을 다뤄야 해서else가 필요하지만enum을 전부 짚으면 뺄 수 있고,enum·Boolean은 문으로 써도 빠짐없이 짚어야 한다.
| 하는 일 | 자바 (JDK 21) | 코틀린 | 확인한 방법 |
|---|---|---|---|
| 분기 하나만 실행 | 옛 case 1: 은 break 필요 · 화살표 case 1 -> 는 불필요 |
1 ->. fall-through 없음 |
실행 |
반복 안의 break |
switch 만 빠져나감 |
바깥 반복문을 끝냄 | 실행 |
| 여러 값 | case 1, 2 -> |
1, 2 -> |
실행 |
| 변수·식 조건 | constant expression required |
limit -> · limit * 2 -> 가능 |
javac 에러 · 실행 |
| 같은 값 두 번 | duplicate case label 에러 |
경고, 첫 분기가 이김 | 컴파일 · 실행 |
| 조건 사슬 | if-else if |
인자 없는 when { }. 조건은 Boolean |
실행 · kotlinc 에러 |
| 타입 분기 | case String s -> 새 변수 |
is String -> 스마트 캐스트. 캐스트는 컴파일러가 씀 |
실행 · CFR |
| 가려진 분기 | dominated by a preceding case label 에러 |
경고 없이 앞 분기가 이김 | 컴파일 · 실행 |
| 구간 분기 | Integer 값에 case Integer a when ... · int 값은 에러 |
in 0..12 -> · !in |
javac 에러 · 실행 |
| 식에서 빠진 경우 | does not cover all possible input values |
must be exhaustive. Add an 'else' branch. |
컴파일 에러 |
| 문에서 빠진 경우 | 패턴 switch 문은 does not cover 에러 |
Any·Int 는 그냥 지나감 · enum·Boolean 은 에러 |
컴파일 에러 · 실행 |
enum 을 전부 짚음 |
default 불필요. 실행 시 새 상수면 MatchException |
else 불필요. 문으로 써도 빠지면 에러. 새 상수면 NoWhenBranchMatchedException |
컴파일 에러 · 실행 |
관련 항목
when 이 대신하는 자바의 분기 문법
switch · switch 식 · fall-through · if-else 사슬 · 패턴 매칭 switch · JEP 361 · JEP 441
when 의 분기 조건에 쓰는 코틀린 문법
when · is 연산자 · in 연산자 · 범위 연산자 · 스마트 캐스트 · else 분기
when 식이 빠짐없이 다뤄야 하는 타입
완전성 검사 · enum class · 열거형 · Boolean · sealed class · sealed interface
빠진 분기를 실행 시점에 알리는 예외
NoWhenBranchMatchedException · MatchException · 분리 컴파일
when 이 컴파일되어 바뀌는 JVM 코드
instanceof · 캐스트 · SwitchBootstraps · Intrinsics · 널 검사
클래스 파일을 열어 보는 도구
javap · 디컴파일 · 디컴파일러 · CFR · 클래스 파일