[Must Have 코드팩토리의 플러터 프로그래밍]

04 다트 3.0 신규 문법

avocado8 2024. 4. 26. 22:32

 

 

4.1 레코드

다트 3.0 이상부터 사용할 수 있는 새로운 타입. 포지셔널 파라미터나 네임드 파라미터 중 한가지 방식을 적용하여 사용할 수 있고, 모두 괄호 안에 쉼표로 구분하여 작성한다.

 

4.1.1 포지셔널 파라미터를 이용한 레코드

포지셔널 파라미터로 표시한 타입 순서를 반드시 지켜야 한다.

void main() {
  //첫 번째 값은 String 타입, 두 번째 값은 int 타입인 레코드
  (String, int) cado = ('카도', 6);
  print(cado); //(카도, 6)
}

 

두 개 이상의 값을 조합해서 레코드를 만들 수도 있다. 정의할 수 있는 값의 개수 제한 X

void main() {
  (String, int, bool) cado = ('카도', 6, true);
  print(cado); //(카도, 6, true)
}

 

모든 값이 아닌 특정 순서의 레코드 값만 가져오고 싶다면 $ 를 사용한다. (1부터 시작)

void main() {
  (String, int, bool) cado = ('카도', 6, true);
  print(cado.$1); //카도
  print(cado.$2); //6
}

 

4.1.2 네임드 파라미터를 이용한 레코드

소괄호에 중괄호를 중첩하여 타입과 변수 이름을 쉼표로 구분하고 명시. 입력 순서를 지킬 필요 없음.

void main() {
  ({String name, int age}) cado = (name: '카도', age: 6);
  print(cado); //(age: 6, name:카도)
}

 

4.2 구조 분해

값을 반환받을 때, 반환된 타입을 그대로 복제해서 타입 내부에 각각의 값을 직접 추출해오는 문법

void main() {
  //리스트 구조분해
  final [cado, nana]= ['카도',' 나나'];
  print(cado); //카도
  
  //리스트에서 스프레드 연산자 이용
  final numbers = [1,2,3,4,5,6,7,8];
  final [x,y, ..., z] = numbers;
  print('$x, $y, $z'); //1, 2, 8
  
  //맵 구조분해
  final cadoMap = {'name':'카도', 'age':6};
  final {'name': name, 'age': age} = cadoMap; //동일한 구조로 구조분해
  print('name : $name, age: $age'); //name: 카도, age: 6
}
void main() {
  //클래스 구조분해
  final nana = Idol(name: '나나', age: 4);
  //클래스의 생성자 구조와 똑같이 구조분해
  final Idol(name: name, age: age) = nana;
  print(name); //나나
  print(age); //4
}

class Idol {
  final String name;
  final int age;
  Idol({
    required this.name,
    required this.age,
  });
}

 

4.3 switch문

4.3.1 표현식 기능

코드는 표현식(expression) 과 문(statement)로 나눌 수 있다.

표현식 : 어떠한 값을 만들어내는 코드

문: 기본 단위이자 가장 작은 코드 실행 단위. 컴퓨터에 내리는 명령.

표현식 여러 개가 모여 문이 되며, 문에는 선언문, 할당문, 반복문 등이 있다.

다트 3.0부터는 switch문을 함수처럼 사용해 직접 값을 반환받을 수 있는 절 기능이 추가되었다.

void main() {
  String dayKor = '월';
  
  String dayEng = switch(dayKor){
      //switch문 조건에 맞을 때 값 반환
      '월' => 'Monday',
      '화' => 'Tuesday',
      '수' => 'Wednesday',
      '목' => 'Thursday',
      '금' => 'Friday',
      '토' => 'Saturday',
      '일' => 'Sunday',
      _ => 'Not Found', //defaulT
  };
  print(dayEng); //Monday
}

 

4.3.2 패턴 매칭

switch문을 사용한 패턴매칭으로 복잡한 조건 형성 가능

void switcher(dynamic anything){
  switch(anything){
    case 'aaa': //정확히 'aaa' 문자열만 매칭
      print('match: aaa');
      break;
    case [1,2]: //정확히 [1,2] 리스트만 매칭
      print('match: [1,2]');
      break;
    case [_, _, _]: //3개의 값이 들어있는 리스트 모두 매치
      print('match [_,_,_]');
      break;
    case [int a, int b]: //첫번째, 두번째 값에 int가 입력된 리스트 매치
      print('match: [int $a, int $b]');
      break;
    case (String a, int b): //첫번째 String, 두번째 int가 입력된 레코드
      print('match: (String $a, it $b)');
      break;
    default:
      print('no match');
  }
}

 

4.3.3 엄격한 검사

코드가 입력받을 수 있는 모든 조건을 전부 확인하고 있는지 체크하는 기술

다트 3.0에서는 switch문에 엄격한 검사가 추가되어 모든 조건을 확인하고 있는지 빌드할 때 확인할 수 있다.

void main() {
  bool? val; //val에 입력될 수 있는 값: true, false, null
  switch(val){
    case true:
      print('true');
    case false:
      print('false');
    //null조건을 입력하지 않으면 non exhaustive switch statement 에러 발생. null이나 default case 추가 필요
  }
}

 

4.3.4 보호 구문

switch문에 when 키워드로 보호구문(guard clause) 추가 가능

when 키워드는 boolean으로 반환할 조건을 각 case문에 추가할 수 있으며 when 키워드 뒤에 오는 조건이 true를 반환하지 않으면 case 매치가 되지 않음.

void main() {
  (int a, int b) val = (1,-1);
  switch(val){
    case(1, _) when val.$2 > 0: //-1 > 0이 false이기에 매치X
      print('1, _');
      break;
    default:
      print('default');
  }
}

 

4.4 클래스 제한자

추가된 클래스 제한자 : base, final, interface, sealed, mixin

모든 클래스 제한자는 calss 키워드 앞에 명시한다.

클래스 제한자를 명시한 클래스는 해당 클래스를 사용하는 파일이 아닌 다른 파일에 선언해야 정상 작동한다.

 

4.4.1 base 제한자

base 클래스의 기능을 강제하는 제한자.

base 키워드를 사용한 클래스는 오직 상속만 할 수 있게 된다.

base 클래스가 아닌 자식 클래스는 꼭 base, final 또는 sealed 제한자를 함께 사용해야 한다.

//1_a.dart
base class Parent {}
//1_b.dart
import '1_a.dart';

//인스턴스화 가능
Parent parent = Parent();

base class Child extends Parent {}

//오류 - base, sealed, final 중 하나 필요
class Child2 extends Parent {}

//오류 - base 클래스는 implement 불가
class Child3 implements Parent {}

 

4.4.2 final 제한자

같은 파일에서 상속(extend)와 재정의(implement)할 수 있지만 외부 파일에서는 할 수 없다. (인스턴스화만 가능)

base 제한자의 기능을 모두 포함한다.

 

4.4.3 interface 제한자

클래스를 외부 파일에서 상속받지 못하고 재정의만 할 수 있도록 제한하는 역할.

//1_a.dart
interface class Parent {}
//1_b.dart
import '1_a.dart';

//인스턴스화 가능
Parent parent = Parent();

//오류 - 상속 불가
class Child extends Parent {}

//정상 - implement는 가능
class Child3 implements Parent {}

 

4.4.4 sealed 제한자

sealed 클래스를 파일 외부에서 상속, 재정의, 인스턴스화할 수 없도록 제한하는 역할.

//1_a.dart
sealed class Parent {}
//1_b.dart
import '1_a.dart';

//오류 - 인스턴스화 불가
Parent parent = Parent();

//오류 - 상속 불가
class Child extends Parent {}

//오류 - implement 불가
class Child3 implements Parent {}

 

4.4.5 mixin 제한자

일반 mixin과 같은 역할을 하면서도 상속할 수 있음.

mixin class MixinExample {}

//상속 가능
class Child1 extends MixinExample {}

//mixin 사용 가능
class Child2 with MixinExample {}