[Dart] 4. Classes

Speedwell🍀·2023년 9월 7일
0
post-thumbnail

[출처] Nomadcoders - Dart 시작하기

Dart에서 가장 중요!

function내에서 variable을 사용할 때는 데이터 타입을 꼭 명시해줄 필요 없었음 (var)

class 선언할 때는 타입 꼭 명시해줘야 함!

class Player {
  String name = 'atlns';
  int xp = 1500;
}

void main() {
  var player = Player();
  print(player.name);
  player.name = 'snlta';
  print(player.name); // Player의 이름이 변경됨
}

위의 예시에서는 Player의 이름을 바꿀 수 있었지만, 바꾸지 못하게 하려면 final 키워드 이용하면 됨

스크린샷 2023-09-07 오후 2 19 40

다른 언어처럼 this를 사용해도 동작하지만 Dart에서는 Class method 내에서의 this는 권장하지X

class Player {
  final String name = 'atlns';
  int xp = 1500;
  
  void sayHello(){
  // print("Hi my name is ${this.name}");
  // 다른 언어처럼 this를 사용해도 동작하지만
  // Dart에서는 Class method 내에서의 this는 권장하지X
    print("Hi, my name is $name");
  }
}

void main() {
  var player = Player();
  player.sayHello();
}

예외) method 내에 같은 이름의 variable이 있는 경우(variable과 class property의 이름이 겹치는 경우) this 사용

class Player {
  final String name = 'atlns';
  int xp = 1500;
  
  void sayHello(){
    var name = 'ana';

    print("name: $name"); // ana
    print("Hi, my name is ${this.name}"); // atlns
  }
}

void main() {
  var player = Player();
  player.sayHello();
}

Constructors

위의 예제를 변경해 argument로 player의 이름과 xp를 전달해서 새로운 Player를 생성해보자

// [효율적이지 않은 코드]
class Player {
  // late 없으면 Error
  // non-nullable instance field 'name', 'xp' must be initialized
  late final String name;
  late int xp;
  
  Player(String name, int xp){
    this.name = name;
    this.xp = xp;
  }
  
  void sayHello(){
    print("Hi, my name is $name");
  }
}

void main() {
  var player = Player('atlns', 1500);
  player.sayHello();
  var player2 = Player('bada', 2400);
  player2.sayHello();
}

하지만 너무 반복적인 작업

name이 String이고, xp가 int 타입이라고 이미 명시했기 때문에 late을 지우고 아래와 같이 작성해주는 게 더 효율적!

class Player {
  final String name;
  int xp;
  
  Player(this.name, this.xp);
  
  void sayHello(){
    print("Hi, my name is $name");
  }
}

void main() {
  var player = Player('atlns', 1500);
  player.sayHello();
  var player2 = Player('bada', 2400);
  player2.sayHello();
} 

Named Constructor Parameters

class가 클 때 위와 같은 positional parameter를 사용하면 컨트롤이 어려움 (순서를 지켜서 argument 넘겨줘야 되니까)

named parameter를 가진 constructor로 바꿔주자!

class Player {
  final String name;
  int xp;
  String team;
  int age;

  Player({ // 중괄호 추가
    required this.name,
    required this.xp,
    required this.team,
    required this.age,
  }); 
  // required 키워드 없으면 Error (다른 해결 방법은 초기값 대입이지만 여기선 좋지 않은 해결방법)
  // The parameter '' can't have a value of 'null' because of its type, but the implicit value is 'null'.

  void sayHello() {
    print("Hi, my name is $name");
  }
}

void main() {
  var player = Player(
    name: 'atlns',
    xp: 1500,
    team: 'red',
    age: 23,
  );
  player.sayHello();
  var player2 = Player(
    name: 'bada',
    age: 28,
    team: 'blue',
    xp: 2400,
  );
  player2.sayHello();
}

Positional parameter는 required가 default이기 때문에 필요X


Named Constructors

Flutter에서 정말 많이 사용됨!

Player.createBluePlayer({
    // 콜론(:)을 사용해서 Player 클래스 초기화
    required String name,
    required int age,
  })  : this.age = age,
        this.name = name,
        this.team = 'blue',
        this.xp = 0;

// positional parameter는 required가 default
Player.createRedPlayer(String name, int age)
    : this.age = age,
      this.name = name,
      this.team = 'red',
      this.xp = 0;

위와 같은 형태를 사용해서 named constructor 생성

class Player {
  final String name;
  int xp, age;
  String team;

  Player({
    required this.name,
    required this.xp,
    required this.team,
    required this.age,
  });

  Player.createBluePlayer({
    // 콜론(:)을 사용해서 Player 클래스 초기화
    required String name,
    required int age,
  })  : this.age = age,
        this.name = name,
        this.team = 'blue',
        this.xp = 0;

  // positional parameter는 required가 default
  Player.createRedPlayer(String name, int age)
      : this.age = age,
        this.name = name,
        this.team = 'red',
        this.xp = 0;

  void sayHello() {
    print("Hi, my name is $name");
  }
}

void main() {
  var player = Player.createBluePlayer(
    name: 'atlns',
    age: 23,
  );
  player.sayHello();
  var player2 = Player.createRedPlayer('bada', 28);
  player2.sayHello();
}

API data를 사용할 경우 아래처럼 작성할 수 있음

class Player {
  final String name;
  int xp;
  String team;

  Player.fromJson(Map<String, dynamic> playerJson)
      : name = playerJson['name'],
        xp = playerJson['xp'],
        team = playerJson['team'];
  
  void sayHello() {
    print("Hi, my name is $name");
  }
}

void main() {
  var apiData = [
    {
      "name": "atlns",
      "team": "red",
      "xp": 0,
    },
    {
      "name": "bada",
      "team": "red",
      "xp": 0,
    },
    {
      "name": "cloud",
      "team": "red",
      "xp": 0,
    },
  ];
  
  apiData.forEach((playerJson) {
    var player = Player.fromJson(playerJson);
    player.sayHello();
  });
} 

Cascade Notation

class Player {
  String name, team;
  int xp;

  Player({
    required this.name,
    required this.xp,
    required this.team,
  });

  void sayHello() {
    print("Hi, my name is $name");
  }
}

void main() {
  var atlns = Player(name: 'atlns', xp: 1500, team: 'red');
  atlns.name = 'bada';
  atlns.xp = 250000;
  atlns.team = 'blue';
}

위의 경우 main에서 반복되는 코드를 줄이기 위해 cascade notation을 쓸 수 있다.

var atlns = Player(name: 'atlns', xp: 1500, team: 'red')
  ..name = 'bada'
  ..xp = 250000
  ..team = 'blue'
  ..sayHello(); 

Player object를 생성한 뒤 세미콜론을 작성하지 않은 채로, ..을 사용하면 이는 바로 앞의 class를 가리키게 된다.

만약 Player object를 생성한 후 cascade notation을 뒤에서 사용하고 싶다면 아래와 같이 쓸 수 있다.

var atlns = Player(name: 'atlns', xp: 1500, team: 'red');
var bada = atlns
..name = 'bada'
..xp = 250000
..team = 'blue'
..sayHello(); 

Enums

Flutter에서 많이 사용됨

데이터를 잘못 적는 실수를 방지해줌

enum Team { red, blue } // "" 안 써줘도 됨
enum XPLevel { beginner, medium, pro }

class Player {
  String name;
  XPLevel xp;
  Team team; // 이제 String이 아닌 enum

  Player({
    required this.name,
    required this.xp,
    required this.team,
  });

  void sayHello() {
    print("Hi, my name is $name");
  }
}

void main() {
  var atlns = Player(name: 'atlns', xp: XPLevel.beginner, team: Team.red);
  var bada = atlns
  ..name = 'bada'
  ..xp = XPLevel.pro
  ..team = Team.blue
  ..sayHello();
}

Abstract Classes

Flutter에서 많이 쓰이지 않지만 유용

추상화 클래스는 다른 클래스들이 어떤 blueprint를 가지고 있어야 하는지 정의해주기 때문에 매우 유용

  • 수많은 blueprint에 메소드의 이름과 반환 타입만 정해서 정의하고 파라미터 설정할 수 있음
    ⇒ 다른 클래스에서 상속/확장(extends)

추상화 클래스는 이를 상속받는 모든 클래스가 가지고 있어야 하는 메소드를 정의/강제하기 때문에, 상속받는 클래스에서 해당 메소드를 구현하지 않으면 에러가 발생한다.

abstract class Human {
  void walk();
}

...

class Player extends Human{
  ...
  
  void walk(){
    print("I'm walking");
  }

	...
}

class Coach extends Human{
  void walk(){
    print('The coach is walking');
  }
}

Inheritance

Flutter에서 자주 쓰이진 않지만 꼭 알아둬야 하는 중요한 개념

부모클래스의 생성자 함수를 호출해야 한다.

자식클래스는 부모클래스의 instance가 되고, 부모클래스의 property와 메소드를 가져온다.

class Human {
  final String name;
  
  Human({required this.name});
  
  void sayHello() {
    print("Hi, my name is $name");
  }
}

enum Team { red, blue }

class Player extends Human {
  final Team team;
  
  Player({
    required this.team,
    required String name,
    // Player 생성자 함수에 있는 name을 부모클래스인 Human으로 전달
    // super 키워드를 통해 확장한 부모 클래스와 상호작용 (부모 클래스의 프로퍼티에 접근하거나 메소드 호출 가능하게)
  }) : super(name: name); // 객체지향프로그래밍(OOP) 개념
  
  
  void sayHello() {
    super.sayHello();
    print("and I play for $team");
  }
}

void main() {
  var player = Player(team: Team.red, name:'atlns');
  player.sayHello();
}

제일 중요한 점은, 확장한 부모 클래스가 생성자를 포함하고 있을 때, 그 클래스를 자식 클래스가 사용하려면 필요한 값을 전달해야 하고 부모 클래스의 생성자를 호출해줘야 함super(name: name)


Mixins

Flutter에서 자주 사용됨

Mixin: 생성자가 없는 클래스 ⇒ super 필요X

with 키워드로 Mixin 클래스의 property와 method를 가져올 수 있음

클래스에 property 추가할 때 매우 유용

Mixin의 핵심은 여러 클래스에 재사용이 가능하다는 점

mixin Strong {
  final double strengthLevel = 1234.56;
}

mixin QuickRunner {
  void runQuick() {
    print("runnnnn");
  }
}

mixin Tall {
  final double height = 176.4;
}

enum Team { red, blue }

class Horse with Strong, QuickRunner {}

class Kid with QuickRunner {}

class Player with Strong, QuickRunner, Tall {
  final Team team;

  Player({
    required this.team,
    required String name,
  });
}

void main() {
  var player = Player(
    name: 'atlns',
    team: Team.red,
  );
  player.runQuick();
}

0개의 댓글