-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
366 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,366 @@ | ||
# 04. 다트 3.0 신규 문법 | ||
|
||
**학습 목표** | ||
|
||
- 플러터 3.0 버전 부터는 다트 3.0 버전 이상을 사용함. | ||
- 그리고 다트 언어의 메이저 버전이 3으로 업데이트되면서 새로 추가된 문법들이 생김. | ||
|
||
### 4.1. 레코드 | ||
|
||
- Record: 다트 3.0 이상부터 사용할 수 있는 새로운 타입. | ||
- 레코드는 positional parameter 또는 named parameter 중 한 가지 방식을 적용하여 사용할 수 있음. | ||
|
||
**4.1.1. 포지셔널 파라미터를 이용한 레코드** | ||
|
||
- 포지셔널 파라미터를 이용한 레코드는 포지셔널 파라미터로 표시한 타입 순서를 반드시 지켜야 함. | ||
- 다음은 String, int 순서로 데이터를 입력해야 하는 레코드를 선언하는 예: | ||
|
||
```dart | ||
void main() { | ||
// 정확한 위치에 어떤 타입의 값이 입력될지 지정할 수 있음. | ||
(String, int) roo = ('roo', 10); | ||
print(roo); // 결괏값: ('roo', 10) | ||
} | ||
``` | ||
|
||
- 레코드에 정의한 순서대로 타입을 입력하지 않으면 에러 발생함. | ||
- 두 개 이상의 값을 조합해서 레코드를 만들 수도 있음. 레코드에 정의할 수 있는 값의 개수에는 제한이 없음. | ||
- 레코드의 모든 값을 사용하지 않고 특정 순서의 레코드 값을 가져오고 싶다면 `$` 를 사용하면 됨. | ||
|
||
```dart | ||
void main() { | ||
(String, int, bool) roo = ('roo', 10, true); | ||
print(roo.$1); // roo | ||
print(roo.$2); // 10 | ||
print(roo.$3); // true | ||
} | ||
``` | ||
|
||
**4.1.2. 네임드 파라미터를 이용한 레코드** | ||
|
||
- 네임드 파라미터는 소괄호에 중괄호를 중첩하여 타입과 변수 이름을 쉼표로 구분하고 명시해줘야 함. | ||
|
||
```dart | ||
void main() { | ||
({String name, int age}) roo = (name: 'roo', age: 10); | ||
print(roo); // (age: 10, name: roo) | ||
} | ||
``` | ||
|
||
## 4.2. 구조 분해 | ||
|
||
- 구조 분해(destructuring)는 값을 반환받을 때 단순히 하나의 변수로 받아오지 않음. | ||
- 반환된 타입을 그대로 복제해서 타입 내부에 각각의 값을 직접 추출해오는 문법. | ||
|
||
4.2.1. 리스트에서의 구조 분해 사용 | ||
|
||
```dart | ||
void main() { | ||
final [roo, loo] = ['roo', 'loo']; | ||
print(roo); // roo | ||
print(loo); // loo | ||
} | ||
``` | ||
|
||
4.2.2. 리스트에서의 스프레드 연산자를 이용한 구조 분해 사용 | ||
|
||
```dart | ||
void main() { | ||
final numbers = [1, 2, 3, 4, 5, 6, 7, 8]; | ||
// 스프레드 연산자를 사용해서 중간의 값들을 버리기 | ||
final [x, y, ..., z] = numbers; | ||
print(x); // 1 | ||
print(y); // 2 | ||
print(z); // 8 | ||
} | ||
``` | ||
|
||
4.2.3. 맵에서의 구조 분해 사용 | ||
|
||
```dart | ||
void main() { | ||
final rooMap = {'name': 'roo', 'age': 10}; | ||
// 위 구조와 같은 구조로 구조 분해하기 | ||
final {'name': name, 'age': age} = rooMap; | ||
print('name: $name'); // name: roo | ||
print('age: $age'); // age: 10 | ||
} | ||
``` | ||
|
||
4.2.4. 클래스에서의 구조 분해 사용 | ||
|
||
```dart | ||
void main() { | ||
final roo = Idol(name: 'roo', age: 10); | ||
// 클래스의 생성자 구조와 똑같이 구조 분해하기 | ||
final Idol(name: name, age: age) = roo; | ||
print(name); | ||
print(age); | ||
} | ||
``` | ||
|
||
## 4.3. switch 문 | ||
|
||
- switch 문은 다트 언어가 3.0. 버전으로 업데이트되면서 스위치 표현식(switch expression), 패턴 매칭(pattern matching), 완전 확인(exhaustiveness checking), 가드 절(guard clause) 네 가지가 추가 됨. | ||
|
||
**4.3.1. 표현식 기능** | ||
|
||
- 코드는 표현식(expression)과 문(statement)으로 나눌 수 있음. | ||
- expression: | ||
- 표현식은 어떠한 값을 만들어내는 코드. | ||
- 예를 들어 1 + 1은 값 2를 만드는 표현식. | ||
- 이처럼 표현식이 평가되면 새로운 값을 생성하거나 기존 값을 참조함. | ||
- statement: | ||
- 기본 단위이자 가장 작은 코드 실행 단위로 명령문(컴퓨터에 내리는 명령)을 뜻함. | ||
- 표현식 여러 개가 모여 statement가 됨. | ||
- statement의 종류: 선언문, 할당문, 반복문 | ||
- 다트 3.0부터는 switch 문을 함수처럼 사용하여 직접 값을 반환받을 수 있는 절 기능이 추가됨. | ||
|
||
```dart | ||
void main() { | ||
String dayKor = '월요일'; | ||
// switch문이 함수처럼 값을 반환함. | ||
String dayEnglish = switch (dayKor) { | ||
// '=>'를 사용하면 switch 문 조건에 맞을 때 값을 반환할 수 있음. | ||
'월요일' => 'Monday', | ||
'화요일' => 'Tuesday', | ||
// _ 는 default와 같은 의미로 사용됨. | ||
_ => 'Not Found', | ||
}; | ||
print(dayEnglish); // Monday | ||
} | ||
``` | ||
|
||
**4.3.2. 패턴 매칭** | ||
|
||
- 패턴 매칭(pattern matching) = 다트 3.0에 추가된 기능. 특히 switch 문을 사용할 때 패턴 매칭을 통해서 더욱 복잡한 조건을 형성할 수 있어 유용함. | ||
|
||
```dart | ||
void switcher(dynamic anything) { | ||
switch (anything) { | ||
// 정확히 'aaa' 문자열만 매칭 | ||
case 'aaa': | ||
print('match: aaa'); | ||
break; | ||
// 정확히 [1, 2] 리스트만 매칭 | ||
case [1, 2]: | ||
print('match [1, 2]'); | ||
break; | ||
// 3개의 값이 들어 있는 리스트를 모두 매칭 | ||
case [_,_,_]: | ||
print('match [_,_,_]'); | ||
break; | ||
// 첫 번째와 두 번째 값에 int가 입력된 리스트를 매칭 | ||
case [int a, int b]: | ||
print('match: [int $a, int $b]'); | ||
break; | ||
// 첫 번째 값에 String, 두 번째 값에 int가 입력된 Record 타입 매칭 | ||
case (String a, int b): | ||
print('match: (String: $a, int: $b)'); | ||
break; | ||
default: | ||
print('no match'); | ||
} | ||
} | ||
void main() { | ||
switcher('aaa'); | ||
switcher([1, 2]); | ||
switcher([3, 4, 5]); | ||
switcher([6, 7]); | ||
switcher(('roo', 10)); | ||
switcher(8) | ||
} | ||
// 결괏값: | ||
// match: aaa | ||
// match [1, 2] | ||
// match [_,_,_] | ||
// match: [int 6, int 7] | ||
// match: (String: roo, int: 10) | ||
// no match | ||
``` | ||
|
||
**4.3.3. 엄격한 검사** | ||
|
||
- 엄격한 검사(exhaustiveness checking)는 코드가 입력받을 수 있는 모든 조건을 전부 확인하고 있는지 체크하는 기술. | ||
- 다트 3.0에서는 switch 문에 엄격한 검사가 추가되어 모든 조건을 확인하고 있는지 빌드할 때 확인 가능. | ||
|
||
```dart | ||
void main() { | ||
// val에 입력될 수 있는 값은 true, false, null | ||
bool? val; | ||
// null 조건을 입력하지 않았기 때문에 | ||
// non exhaustive switch statement 에러 출력 | ||
// null case를 추가하거나 default case를 추가해야 에러가 사라짐. | ||
switch (val) { | ||
case true: | ||
print('true'); | ||
case false: | ||
print('false'); | ||
} | ||
} | ||
``` | ||
|
||
4.3.4. 보호 구문 | ||
|
||
- switch문에는 when 키워드로 보호 구문(guard clause)를 추가할 수 있도록 업데이트 됨. | ||
- when 키워드는 boolean으로 반환할 조건을 각 case문에 추가할 수 있으며, | ||
- when 키워드 뒤에 오는 조건이 true를 반환하지 않으면 case 매치가 안됨. | ||
|
||
```dart | ||
void main() { | ||
(int a, int b) val = (1, -1); | ||
// default가 출력됨. | ||
// 만약 b 값을 0 이상으로 변경하면, | ||
// 1, _ 를 출력함. | ||
switch (val) { | ||
case (1, _) when val.$2 > 0: | ||
print('1, _'); | ||
break; | ||
default: | ||
print('default'); | ||
} | ||
} | ||
``` | ||
|
||
## 4.4. 클래스 제한자 | ||
|
||
- class modifiers: 다트 3.0에 추가된 클래스 제한자는 base, final, interface, sealed, mixin | ||
- 모든 클래스 제한자는 class 키워드 앞에 명시함. | ||
- 클래스 제한자를 명시한 클래스는 해당 클래스를 사용하는 파일이 아닌 다른 파일에 선언해야 정상으로 기능이 작동함. | ||
|
||
**4.4.1 base 제한자** | ||
|
||
- base 제한자는 base 클래스의 기능을 강제하는 제한자. | ||
- base 키워드를 사용하게 되면 해당 클래스는 오직 상속만 할 수 있게 됨. | ||
- 그리고 base 클래스가 아닌 자식 클래스는 꼭 base, final 또는 sealed 제한자를 함께 사용해줘야 함. | ||
|
||
```dart | ||
// lib/4.4/1_a.dart | ||
base class Parent {} | ||
``` | ||
|
||
```dart | ||
// lib/4.4/1_b.dart | ||
import '1_a.dart'; | ||
// 인스턴스화 가능 | ||
Parent parent = Parent(); | ||
// rksmd | ||
base class Child extends Parent {} | ||
// subtype of base or final is not base final or sealed 에러 출력 | ||
// base / sealed / final 제한자 중 하나가 필요. | ||
class Child2 extends Parent {} | ||
// subtype of base or final is not base final or sealed 에러 출력 | ||
// base 클래스는 implement가 불가능. | ||
class Child3 implements Parent {} | ||
``` | ||
|
||
**4.4.2. final 제한자** | ||
|
||
- final 제한자를 사용하면 같은 파일에서 상속(extend)과 재정의(implement)를 할 수 있지만 외부 파일에서는 할 수 없음. | ||
- 그리고 final 제한자는 base 제한자의 기능을 모두 포함함. | ||
|
||
```dart | ||
// lib/4.4/2_a.dart | ||
final class Parent {} | ||
``` | ||
|
||
```dart | ||
// lib/4.4/2_b.dart | ||
import '2_a.dart'; | ||
// 인스턴스화 가능 | ||
Parent parent = Parent(); | ||
// extend 불가능 | ||
class Child extends Parent {} | ||
// implement 불가능 | ||
class Child2 implements Parent {} | ||
``` | ||
|
||
**4.4.3. interface 제한자** | ||
|
||
- interface 제한자는 클래스를 외부 파일에서 상속받지 못하고 재정의만 할 수 있도록 제한하는 역할을 함. | ||
|
||
```dart | ||
// lib/4.4/3_a.dart | ||
interface class Parent {} | ||
``` | ||
|
||
```dart | ||
// lib/4.4/3_b.dart | ||
import '3_a.dart'; | ||
// 인스턴스화 가능 | ||
Parent parent = Parent(); | ||
// extend 불가능 | ||
class Child1 extends Parent {}; | ||
// implement 가능 | ||
class Child2 implements Parent {} | ||
``` | ||
|
||
4.4.4. sealed 제한자 | ||
|
||
- sealed 제한자는 sealed 클래스를 파일 외부에서 상속, 재정의 그래고 인스턴스화 할 수 없도록 제한함. | ||
|
||
```dart | ||
// lib/4.4/4_a.dart | ||
sealed class Parent{} | ||
``` | ||
|
||
```dart | ||
import '4_a.dart'; | ||
// 인스턴스화 불가능 | ||
Parent parent = Parent(); | ||
// extend 불가능 | ||
class Child1 extends Parent {} | ||
// implements 불가능 | ||
class Child2 implements Parent {} | ||
``` | ||
|
||
**4.4.5. mixin 제한자** | ||
|
||
- 다트 3.0부터는 mixin을 클래스에 사용할 수 있게 됨. | ||
- 일반 mixin과 같은 역할을 하면서도 상속할 수 있다는 장점. | ||
|
||
```dart | ||
// lib/4.4/5.dart | ||
mixin class MixinExample {} | ||
// extend 가능 | ||
class Child1 extends MixinExample {} | ||
// mixin으로 사용 가능 | ||
class Child2 with MixinExample {} | ||
``` | ||
|
||
### 핵심 요약 | ||
|
||
1. 레코드: 새로운 타입으로 named parameter와 positional parameter 사용해서 생성 가능 | ||
2. 구조 분해: 타입 내부의 각각의 값을 직접 추출해오는 문법 | ||
3. switch 문: 표현식, 패턴 매칭, 완전 확인, 가드 절이 추가되어 다양한 방법으로 조건을 확인할 수 있음. | ||
4. 클래스 제한자**:** 객체지향 프로그래밍 언어의 특징 중 하나인 클래스의 고유성을 위해 다양한 클래스 제한자가 추가됨. |