분기
분기 (Branches)
코드의 흐름을 조건에 따라 갈라지게 만들고 싶을 때가 있어요. Dart에서는 if 문, if-case 문, switch 문과 식을 써서 분기를 만들 수 있어요. 이 페이지에서는 이 세 가지를 하나씩 살펴볼게요.
출처: Dart 공식 문서
본문
분기를 다루는 것 외에도 Dart에서는 반복(loops)과 예외(exception)로도 흐름 제어를 할 수 있어요. 하지만 이 페이지의 핵심은 분기예요.
if
Dart의 if 문은 선택적으로 else 절을 가질 수 있어요. if 뒤 괄호 안의 조건은 반드시 불리언(boolean)으로 평가되는 표현식이어야 해요.
if (isRaining()) {
you.bringRainCoat();
} else if (isSnowing()) {
you.wearJacket();
} else {
car.putTopDown();
}
if를 표현식 문맥에서 쓰는 방법은 조건 표현식(Conditional expressions) 문서에서 다뤄요.
if-case
Dart의 if 문은 case 절 뒤에 패턴을 붙여 쓸 수 있어요.
if (pair case [int x, int y]) return Point(x, y);
패턴이 값과 일치하면, 패턴이 정의한 변수들이 유효 범위 안에 들어온 상태로 해당 분기가 실행돼요. 위 예시에서 리스트 패턴 [int x, int y]가 값 pair와 일치하므로, 패턴이 정의한 x, y 변수를 가진 채로 return Point(x, y) 분기가 실행됩니다.
일치하지 않으면, 있다면 else 분기로 진행해요.
if (pair case [int x, int y]) {
print('Was coordinate array $x,$y');
} else {
throw FormatException('Invalid coordinates.');
}
if-case 문은 하나의 패턴에 대해 매칭하고 구조 분해(destructure)하는 방법을 제공해요. 값을 여러 패턴에 대해 검사하려면 switch를 사용하세요. if 문 안의 case 절은 언어 버전이 최소 3.0이어야 해요.
switch 문 (Switch statements)
switch 문은 값 표현식을 일련의 case들과 비교해서 평가해요. 각 case 절은 값이 일치해야 하는 패턴이에요. case에는 어떤 종류의 패턴이든 쓸 수 있어요.
값이 어떤 case의 패턴과 일치하면 그 case 본문이 실행돼요. 비어 있지 않은 case 절은 완료 후 switch의 끝으로 점프해요. break 문이 필요하지 않다는 뜻이에요. 비어 있지 않은 case 절을 끝내는 다른 유효한 방법으로는 continue, throw, return 문이 있어요.
어떤 case도 일치하지 않을 때 코드를 실행하려면 default 또는 와일드카드 _ 절을 써요.
var command = 'OPEN';
switch (command) {
case 'CLOSED':
executeClosed();
case 'PENDING':
executePending();
case 'APPROVED':
executeApproved();
case 'DENIED':
executeDenied();
case 'OPEN':
executeOpen();
default:
executeUnknown();
}
빈 case는 다음 case로 통과(fall through)해서, case들이 본문을 공유할 수 있게 해줘요. 통과하지 않는 빈 case에는 break를 본문으로 쓰세요. 순차적이지 않은 통과가 필요하다면 continue 문과 레이블(label)을 쓸 수 있어요.
switch (command) {
case 'OPEN':
executeOpen();
continue newCase; // Continues executing at the newCase label.
case 'DENIED': // Empty case falls through.
case 'CLOSED':
executeClosed(); // Runs for both DENIED and CLOSED,
newCase:
case 'PENDING':
executeNowClosed(); // Runs for both OPEN and PENDING.
}
논리 OR 패턴(logical-or pattern)을 쓰면 case들이 본문이나 가드를 공유하게 할 수 있어요. 패턴과 case 절에 대해 더 자세히 알고 싶다면 패턴 문서의 Switch statements and expressions 부분을 확인하세요.
switch 식 (Switch expressions)
switch 식 은 일치하는 case의 본문 표현식을 기반으로 값을 생성해요. switch 식은 Dart가 식을 허용하는 모든 곳에서 쓸 수 있어요. 다만 표현식 문(expression statement)의 시작 에서는 예외예요. 예를 들면:
var x = switch (y) { ... };
print(switch (x) { ... });
return switch (x) { ... };
표현식 문의 시작에 switch를 쓰고 싶다면 switch 문을 사용하세요.
switch 식을 쓰면 다음과 같은 switch 문 을:
// Where slash, star, comma, semicolon, etc., are constant variables...
switch (charCode) {
case slash || star || plus || minus: // Logical-or pattern
token = operator(charCode);
case comma || semicolon: // Logical-or pattern
token = punctuation(charCode);
case >= digit0 && <= digit9: // Relational and logical-and patterns
token = number();
default:
throw FormatException('Invalid');
}
이렇게 식 으로 다시 쓸 수 있어요.
token = switch (charCode) {
slash || star || plus || minus => operator(charCode),
comma || semicolon => punctuation(charCode),
>= digit0 && <= digit9 => number(),
_ => throw FormatException('Invalid'),
};
switch 식의 문법은 switch 문과 달라요.
- case는
case키워드로 시작하지 않아요. - case 본문은 일련의 문 대신 단일 표현식이에요.
- 각 case는 본문을 가져야 해요. 빈 case에 대한 암묵적 통과는 없어요.
- case 패턴은 본문과
:대신=>로 구분해요. - case들은
,로 구분돼요(선택적 trailing,허용). - 기본 case는
default와_를 모두 허용하는 대신_만 쓸 수 있어요.
switch 식은 언어 버전이 최소 3.0이어야 해요.
완전성 검사 (Exhaustiveness checking)
완전성 검사는 값이 switch에 들어왔는데 어떤 case와도 일치하지 않을 가능성이 있을 때 컴파일 타임 에러를 보고하는 기능이에요.
// Non-exhaustive switch on bool?, missing case to match null possibility:
switch (nullableBool) {
case true:
print('yes');
case false:
print('no');
}
nullableBool은 null이 될 수 있는데 그 경우를 다루는 case가 없어서 에러가 나요. 기본 case(default 또는 _)는 switch를 통과할 수 있는 모든 가능한 값을 덮어요. 그래서 어떤 타입이든 switch를 완전하게 만들어 주죠.
enum과 sealed 타입은 switch에 특히 유용한데, 기본 case가 없어도 가능한 값들을 모두 알고 완전히 열거할 수 있기 때문이에요. 클래스에 sealed 수정자를 쓰면 그 클래스의 하위 타입을 switch할 때 완전성 검사를 켤 수 있어요.
sealed class Shape {}
class Square implements Shape {
final double length;
Square(this.length);
}
class Circle implements Shape {
final double radius;
Circle(this.radius);
}
double calculateArea(Shape shape) => switch (shape) {
Square(length: var l) => l * l,
Circle(radius: var r) => math.pi * r * r,
};
누군가 Shape의 새 하위 클래스를 추가하면 이 switch 식은 불완전해져요. 완전성 검사가 빠진 하위 타입을 알려주죠. 이렇게 해서 Dart를 함수형 대수 데이터 타입(algebraic datatype) 스타일로 쓸 수 있어요.
가드 절 (Guard clause)
case 절 뒤에 선택적 가드 절을 설정하려면 when 키워드를 써요. 가드 절은 if case, 그리고 switch 문과 식 모두에서 사용할 수 있어요.
// Switch statement:
switch (something) {
case somePattern when some || boolean || expression:
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Guard clause.
body;
}
// Switch expression:
var value = switch (something) {
somePattern when some || boolean || expression => body,
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Guard clause.
}
// If-case statement:
if (something case somePattern when some || boolean || expression) {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Guard clause.
body;
}
가드는 매칭 후에 임의의 불리언 표현식을 평가해요. 이렇게 해서 case 본문을 실행할지 말지 추가 제약을 걸 수 있죠. 가드 절이 false로 평가되면 전체 switch를 빠져나가는 대신 다음 case로 진행돼요.