Skip to content

Commit

Permalink
Create ch04.md
Browse files Browse the repository at this point in the history
  • Loading branch information
deepbig authored Jul 28, 2024
1 parent d9aeec9 commit 4ae8238
Showing 1 changed file with 366 additions and 0 deletions.
366 changes: 366 additions & 0 deletions hong/flutter/코드팩도리의 플러터 프로그래밍/ch04.md
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. 클래스 제한자**:** 객체지향 프로그래밍 언어의 특징 중 하나인 클래스의 고유성을 위해 다양한 클래스 제한자가 추가됨.

0 comments on commit 4ae8238

Please sign in to comment.