본문 바로가기
플러터(flutter)

Dart 기초 - 클래스

by 기계공학 주인장 2023. 4. 9.
반응형

Dart 기초 - Constructor 생성하기

 

다음과 같은 방법으로 생성자를 만들 수 있다.

 

class Player {
  late String name;
  late int score;

  Player(this.name, this.score);
}

void main() {
  var player1 = Player("Lee", 20);
  var player2 = Player("Kim", 30);

  print(player1.score); // 20
  print(player2.score); // 30
  
  // you can change the value.
  player2.score = 50;
  
  print(player2.score);
}

 

코틀린과 유사하면서 조금 다른 모습을 볼 수 있다.

 

위 코드에선 생성자로 지정한 변수를 어느 타이밍에서든 변경할 수 있는데

 

변수에 final 처리를 해서 변경할 수 없게 만들 수 있다.

 

Named Constructor

Named Constructor를 사용하면 일부 생성자에 초기값을 지정할 수 있다.

 

사용 방법은 java에서 사용하는 생성자를 사용한 변수 지정과 유사한 형태를 보인다.

 

class Player {
  late String name, team;
  late int score;

  Player(this.name, this.score);
  
  // each team parameter has default value.
  Player.createBlueTeam({required this.name, required this.score})
      : this.team = "blue";

  Player.createRedTeam({required this.name, required this.score})
      : this.team = "red";
}

void main() {
  var player1 = Player.createBlueTeam(name: 'Lee', score: 50);
  var player2 = Player.createRedTeam(name: 'Kim', score: 50);
}

 

required를 사용해서 반드시 넣어줘야하는 파라미터를 지정하고

 

: 을 사용해서 각각의 파라미터의 기본값을 지정한다는 것이다.


API 데이터 받아서 클래스로 저장하기

일반적으로 API를 통해서 JSON 데이터를 받는데 이를 클래스로 만들어본다.

 

예시 데이터는 다음과 같고

 

  var apiData = [
    {"name": "Lee", "team": "red", "score": 0},
    {"name": "Kim", "team": "blue", "score": 0},
    {"name": "Park", "team": "green", "score": 0}
  ];

 

해당 데이터를 받는 클래스의 파라미터는 다음과 같다.

 

class Player {
  late final String name;
  late int score;
  late String team;
}

 

다음과 같은 방법을 사용해서 Player 클래스에서 json 데이터를 받아서 프로퍼티로 넣어줄 수 있다.

 

class Player {
  final String name;
  int score;
  String team;
  
  // : 를 사용하여 프로퍼티를 초기화
  Player.fromJson(Map<String, dynamic> playerJson):
    name = playerJson['name'],
    score = playerJson['score'],
    team = playerJson['team'];
  
  void hello() {
    print("hello $name / $score / $team");
  }
}

 

참고로 Player 클래스의 프로퍼티에 late를 붙여도 똑같이 동작한다.

 

class Player {
  late final String name;
  late int score;
  late String team;
  
  ~~~~
  
  }

 

이제 apiData를 Player 클래스에 넣어서 hello 함수로 출력해본다.

 

apiData에는 3개의 데이터가 있기 때문에 forEach를 사용해서 하나씩 출력하게 한다.

 

class Player {
  late final String name;
  late int score;
  late String team;
  
  // : 를 사용하여 프로퍼티를 초기화
  Player.fromJson(Map<String, dynamic> playerJson):
    name = playerJson['name'],
    score = playerJson['score'],
    team = playerJson['team'];
  
  void hello() {
    print("hello $name / $score / $team");
  }
}

void main() {
  var apiData = [
    {"name": "Lee", "team": "red", "score": 0},
    {"name": "Kim", "team": "blue", "score": 0},
    {"name": "Park", "team": "green", "score": 0}
  ];
  
  apiData.forEach((playerJson){
    var player = Player.fromJson(playerJson);
    player.hello();
  });
}


// 출력결과
hello Lee / 0 / red
hello Kim / 0 / blue
hello Park / 0 / green

Cascade Notation

만약 클래스 인스턴스의 매개변수를 바꾸고 싶다면 어떻게 해야할까?

 

일반적인 방법으로 생각하면 다음과 같은 방법이 있다.

 

class Player {
  late String name;
  late int score;
  late String team;
  
  Player({
    required this.name,
    required this.score,
    required this.team
  });
}

void main() {
  var player = Player(name: "lee", score: 20, team: "red");
  
  // 각 요소를 하나씩 변경
  player.name = "kim";
  player.score = 30;
  player.team = "blue";
}

 

위에서처럼 인스턴스의 각 요소를 변경하는 방법이 있지만

 

이를 조금 더 간단하게 표현할 수 있다. 이를 Cascade Notation이라고 한다.

 

void main() {
  var player = Player(name: "lee", score: 20, team: "red")
  
  // ..은 player를 의미한다.
  ..name = "kim"
  ..score = 30
  ..team = "blue";
  
  // 이미 정의한 인스턴스라면 이렇게 할 수도 있다.
  player
    ..name = "las"
    ..score = 50
    ..team = "red";
}

Enum

Enum을 사용하면 변수에 넣을 수 있는 값을 제한할 수 있다.

 

다음과 같은 방법으로 Enum을 정의하고 사용할 수 있습니다.

 

enum Team {
  red,
  blue
}

void main() {
  var player = Player(name: "lee", score: 20, team: Team.red)
  
  // ..은 player를 의미한다.
  ..name = "kim"
  ..score = 30
  ..team = Team.blue;
}

Abstract(추상화) 클래스

추상화 클래스는 일반적인 클래스처럼 값을 정의할 때 사용하는 것이 아니라

 

다른 클래스에 상속되어서 사용된다.

 

상속된 클래스는 반드시 추상 클래스가 가진 매개변수나 함수를 재정의(override) 해야한다.

 

예를 들면 다음과 같다.

 

abstract class Human {
  void walk();
}

class Player extends Human {
  late String name;
  late int score;
  late Team team;
  
  Player({
    required this.name,
    required this.score,
    required this.team
  });

  // @override 구문은 삭제해도 상관없다.
  @override
  void walk() {
    print("player walking");
  }
}

void main() {
  var player = Player(name: "lee", score: 20, team: Team.red);
  player.walk();
}

 

이처럼 특정 함수에서 특정 기능을 반드시 포함하거나 해야할 때 사용할 수 있는게 abstract class이다.


클래스 상속

객체지향 함수라면 반드시 알아야할 기능이 클래스 상속이다.

 

다음과 같은 클래스가 있다고 생각한다.

 

class Human {
  final String name;
  Human({required this.name});

  void hello() {
    print("my name is $name");
  }
}

 

Human 클래스는 name이라는 매개변수를 필수로 받아야하며

 

Player 클래스에 다음과 같이 상속된다.

 

class Human {
  final String name;
  Human({required this.name});

  void hello() {
    print("my name is $name");
  }
}

enum Team { red, blue }

class Player extends Human {
  final Team team;
  
  // 상속받은 Human에 name을 넘겨줘야하기 때문에 반드시 name을 받게한다.
  Player({required this.team, required String name})
    : super(name: name); // super를 통해 상속하는 클래스에 접근할 수 있다.
  
  // override를 사용하면 함수도 재정의할 수 있다.
  @override
  void hello() {
    super.hello();
    print("my team is : ${team.name}");
  }
}

 

extends를 사용해서 Human 클래스를 Player 클래스에 상속할 수 있으며

 

Human 클래스에서 필요로 하는 name을 넘겨주기 위해

 

Player 클래스에서도 name을 반드시 받아야하는 매개변수로 설정한다.

 

받은 name은 다음과 같이 supuer로 상속한 클래스에 매개변수로 넘겨줄 수 있다.

 

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

 

상속한 클래스는 다음과 같이 사용할 수 있다.

 

class Human {
  final String name;
  Human({required this.name});

  void hello() {
    // my name is lee
    print("my name is $name");
  }
}

enum Team { red, blue }

class Player extends Human {
  final Team team;

  Player({required this.team, required String name})
    : super(name: name);
  
  @override
  void hello() {
    super.hello();
    print("my team is : ${team.name}");
  }
}

void main() {
  var player = Player(
    team: Team.red,
    name: "lee"
  );
  player.team;
  player.name;
  
  // my team is : red
  player.hello();
  
}

Mixins 클래스

Mixins 클래스는 생성자가 없는 클래스를 의미하며

 

Mixins 클래스를 사용하면 해당 클래스 내에 정의된 모든 요소를 가져와서 사용할 수 있다.

 

다음과 같이 다수의 클래스를 정의한다.

 

class Strong {
  final double power = 1500.55;
}

class Runner {
  void run(){
    print("run!");
  }
}

class Tall {
  final double height = 1.75;
}

 

이를 with을 사용하여 Player에 삽입한다.(Mixins)

 

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

  Player({required this.team});
}

 

이렇게 하면 Player로 만든 모든 인스턴스는 with에 들어있는 클래스의 모든 요소를 사용할 수 있게 된다.

 

void main() {
  var player = Player(team: Team.blue);
  
  // with으로 처리된 모든 클래스에 있는 기능을 사용가능
  player.height;
  player.power;
  player.run();
}

 

 

플러터 기초 - UI 구성

플러터의 UI를 어떻게 구성하고 제일 중요한 핵심인 플러터의 UI 구성 어디서 UI를 정의하는지 UI의 구성 정의한 UI 구조를 한 눈에 살펴보기 플러터 UI의 시작 플러터의 UI는 runApp() 함수에서 시작

android-developer.tistory.com

 

반응형


"이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다."


댓글