본문 바로가기

CS/Flutter | Dart

Dart 3.0 업데이트 주요 내용 - Record, Destructuring, pattern matching, mixin class

Record

void main() {
  final res = nameAndAge({
    "name": "다미",
    "age": 26,
  });
  
  print(res);
  print(res.$1);
  print(res.$2);
}

//Record
(String, int) nameAndAge(Map<String, dynamic> json) {
  return (json["name"] as String, json["age"] as int);
}

(다미, 26)
다미
26

 

name은 String, age는 int 타입으로 들어갈 거라고 미리 설계

 

List에서는 res[0] 이런 식으로 받아야 했지만

Tuple에서는 res.$1 이렇게 받으면 됨

 

 

void main() {
  final resPet = getPetWithType();
  for (final item in resPet) {
    print(item.$1);
    print(item.$2);
  }

  final resPet2 = getPetWithType2();
  for (final item in resPet2) {
    print(item.name);
    print(item.age);
  }
}

List<Map<String, dynamic>> getPet() {
  return [
    {"name": "chacha", "age": 6},
    {"name": "mongchi", "age": 2},
  ];
}

List<(String, int)> getPetWithType() {
  return [
    ("chacha", 6),
    ("mongchi", 2),
  ];
}

List<({String name, int age})> getPetWithType2() {
  return [
    (name: "chacha", age: 6),
    (name: "mongchi", age: 2),
  ];
}

chacha
6
mongchi
2
chacha
6
mongchi
2

 

-> Record 형태 사용하면 type과 type의 순서를 무조건 보장받을 수 있음

 변수 개수는 무한히 할 수 있음

 

 

destructuring

void main() {
  final (name, age) = ("dami", 26);
  print(name);
  print(age);
  
  final pets = ['chacha', 'mongchi'];
  final [String a, String b] = pets; //데이터타입 맞춰춰야 에러 안남
  print(a);
  print(b);
  
  final nums = [1, 2, 3, 4, 5, 6];
  final [x, y, ..., z] = nums;
  print(x);
  print(y);
  print(z);
}

dami
26
chacha
mongchi
1
2
6

 

..., 이용해 중간 원소들을 건너뛸 수 있는데 한 리스트 안에 하나만 쓸 수 있음

 

 

void main() {
  final nums = [1, 2, 3, 4, 5, 6];
  final [xx, yy, ...rest, zz] = nums;
  print(rest);
}

[3, 4, 5]

 

...rest 하고 rest 출력하면 리스트에서 건너뛴 값들이 출력됨

 

final [_, y, ...rest2, _] = nums;
이처럼 _ 이용해 값들을 무시할 수도 있음

 

 

void main() {
  final damiMap = {"name": "dami", "age": 26};
  final {"name": damiName, "age": damiAge} = damiMap;
  print(damiName);
  print(damiAge);
}

dami
26

 

final {"name": damiName, "age": damiAge} = damiMap; 

위 부분은 name과 age의 key를 저렇게 이름붙인다는 것

 

 

void main() {
  final damiIdol = Idol(name: "dami", age: 26);
  final Idol(name: damiName, age: damiAge) = damiIdol;
  print(damiName);
  print(damiAge);
}

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

타입을 정확히 맞춰주면 변수 두 번 선언할 필요없이 선언문에서 destructuring해서 이름 정해줄 수 있음

거의 대부분의 데이터 구조에서 사용 가능

 

 

pattern matching

switcher

void main() {
  //validation
  final dami = ("dami", 26);
  final (name as String, age as int) = dami;
  print(name);
  print(age);
  
  switcher("aaa");
  switcher("bbb");
  switcher(['1', '2']);
  switcher([1, 2]);
  switcher([1, 2, 3]);
  switcher([4, 5, 6]);
  switcher([4, 5, 6, 7]);
  switcher([6, 9]);
  switcher([6, '9']);
  switcher(7);
}

void switcher(dynamic anything){
  switch(anything){
    case "aaa":
      print("match: aaa");
    case ['1', '2']:
      print("match: [1, 2]");
    case [_, _, _]:
      print("match: [_, _, _]");
    case [int a, int b]:
      print("match: [int $a, $b]");
    case < 10 && >5: //switcher(7);을 위한 것
      print("match: <10 && >5");
    default:
      print("no match");
  }
}

 

 

void main() {
  print(switcher(5, true));
  print(switcher(7, true));
  print(switcher(7, false));
}

String switcher(dynamic val, bool condition) => switch (val) {
      5 => "match: 5",
      7 when condition => "match 7 and true",
      _ => "no match",
    };

match: 5
match 7 and true
no match

 

바로 반환할 수 있도록 case문을 쓸 수도 있음

 

 

void main() {
  forLooper();
}

void forLooper(){
  final List<Map<String, dynamic>> pets = [
    {"name": "chacha", "age": 6,},
    {"name": "mongchi", "age": 2,},
  ];
  
  for(final pet in pets){
    print(pet["name"]);
    print(pet["age"]);
  }
  
  for(var {"name": name, "age": age} in pets){
    print(name);
    print(age);
  }
}

chacha
6
mongchi
2

 

 

void main() {
  ifMatcher();
}

void ifMatcher(){
  final cat = {"name": "chacha", "age": 6};
  
  if(cat case {"name": String name, "age": int age}){
    print(name);
    print(age);
  }
}

chacha
6

 

if문에서 지정한 패턴이 충족되어야 진행됨

 

 

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

final로 class 선언하면 extends, implement, 또는 mixin으로 사용 불가

 

 

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

base로 선언하면 extend는 가능하지만 implement는 불가

base, sealed, final로 선언된 클래스만 extend가 가능

 

 

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

interface로 선언하면 implement만 가능(extend는 불가)

 

 

sealed class Person {}

class Idol extends Person {}

class Engineer extends Person {}

class Chef extends Person {}

String whoIsHe(Person person) => switch (person) {
      Idol i => "아이돌",
      Engineer e => "엔지니어",
      Chef c => "셰프",
      _ => "나머지",
    };

sealed 클래스는 abstract이면서 final

그리고 pattern matching을 사용할 수 있도록 해 줌

클래스 선언한 것들을 다 써줘야 에러 안뜸

- => "나머지" 이런 식으로라도!

 

 

mixin class

mixin class DogMixin{
  String bark(){
    return "멍멍";
  }
}

class Dog with DogMixin{}

mixin은 extends나 with 사용 불가

-> mixin class도 마찬가지로 사용 불가

 

class는 on 키워드 사용 불가

-> mixin class도 마찬가지로 사용 불가

 

 

 


Tiny Star