[Dart] Classes

gigilee·2024년 5월 21일
0

Flutter 입문

목록 보기
5/10
post-thumbnail

Dart Class

Flutter에서는 모든 것이 class로 이루어져 있기 때문에 class가 매우 중요하다. 아래는 Dart로 class를 구현하고, 구현한 class를 main에서 호출하여 사용하는 예제이다. Dart에서 class의 인스턴스를 생성할 때 (예제에서는 Player 인스턴스를 생성하는 부분) 굳이 new를 붙여 작성하지 않아도 되며, class의 메서드 내에서 전역 변수를 사용할 때는 this를 제외해도 된다. 이 얼마나 편리한가 !!😮

다만 메서드 내에 전역 변수와 동일한 변수명을 가진 변수가 있다면 this.name과 같이 this와 함께 변수를 사용하도록 한다.

class Player{
  final String name = 'gigi';
  int xp = 1500;

  void sayHello(){
    var name = 'kiki';
    print("Hi my name is ${this.name}"); // result gigi
    print("Hi my name is $name"); // result kiki
  }
}

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

Constructors

class 내에 생성자를 생성하면 인스턴스 생성 시 전달한 값으로 인스턴스를 생성할 수 있다. 글을 작성하다 보니 같은 말이 여러 번 들어가는 것 같아서 게슈탈트 붕괴 현상이 조금 생길 것 같지만 무시 부탁합니다^^;;

class Player{
  final String name;
  int xp;

  Player(String name, int xp) {
  	this.name = name;
    this.xp = xp;
  }
}

위와 같이 생성자를 생성할 수 있으며, 생성자는 Player(this.name, this.xp) 와 같이 한 줄로 작성할 수도 있다. Dart의 장점이 드러나는 문법들이 종종 보이는 것 같다.

그리고 코드를 보면, name이 상수로 선언된 것을 확인할 수 있는데 위와 같이 작성하게 되면 실제로는 코드 편집기 내에서 오류를 표시한다.

이때, late의 필요성이 부각된다. final만 단독으로 사용했을 경우 변수 선언과 동시에 값을 할당하여 초기화가 이루어져야 하지만 late를 함께 사용했을 경우 Player 인스턴스를 생성할 때 전달받은 값을 name 에 할당할 수 있다. 따라서 위 상황에서 올바른 코드는 late final String name;이 된다.

Named Constructor Parameters

인스턴스를 생성할 때 파라미터의 개수가 늘어날수록 "어떤 파라미터가 어떤 값을 가져야 하고 얜 뭐고 ···" 하는 상황을 방지하기 위한, 직관성을 향상시킬 수 있는 방법이다. 함수를 생성할 때도 이렇게 파라미터의 이름을 명시할 수 있도록 하는 방법이 있었는데, 생성자를 생성할 때도 동일하다.

역시나 중괄호({}) 생성자의 요소들을 감싸고, 인스턴스 생성 시 파라미터의 이름을 명시하여 값을 던져주면 된다. 이러한 방식은 기본적으로 null 값을 방지할 수 있도록 required를 사용하거나, Default value를 할당하는 방법이 있다.

아참, 여기서 같은 자료형을 가진 변수는 한 번에 선언할 수 있다.
예를 들어 String name, team; 과 같이.

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

// required를 사용하거나, 기본 값을 할당할 수 있다.
  Player({
    required this.name,
    required this.xp,
    required this.team,
    this.age = 99,
  });
}

void main() {
  var player = Player(name: 'gigi', xp: 100, team: 'blue');
}

Named Constructors

class 내부 메서드 우측에 콜론(:)을 사용하여 메서드 호출 시 전달받은 값을 class 내부 지역 변수에 접근 및 할당하여 현재 class를 초기화 하는 방법으로 예제 코드로 확인해보겠다.

이런 편리한 문법을 Syntactic sugar 라고 한다. 신택틱 슈가 라네.

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

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

  Player.createBluePlayer({required String name, required int age})
      : this.name = name,
        this.age = age,
        this.team = 'blue',
        this.xp = 100;

  void sayHello() {
    print("Hi my name is $name and I'm $age");
  }
}

void main() {
  var player = Player.createBluePlayer(name: 'gigi', age: 99);
  player.sayHello();
}

출력 값은 Hi my name is gigi and I'm 99이다.

Cascade Notation

이것 또한 Syntactic sugar으로, 길디 길고 긴 생성자명을 무려 .. 그러니까 점 두 개 로 간략하게 표시할 수 있는 문법이다. 세미콜론(;)은 문장 끝에 온점 찍어주듯이 코드의 마지막 부분에만 찍어주면 된다.

// 이렇게도 되고
void main() {
  var player = Player(name: 'gigi', xp: 100, team: 'blue', age: 99);
  player.name = 'tester';
  player.xp = 999;

// 이렇게도 된다.
  var player2 = Player(name: 'gigi', xp: 100, team: 'blue', age: 99)
  ..name = 'tester2'
  ..xp = 888;
}

Enums

Enum은 옵션의 성질과 같다고도 볼 수 있는데, 어쨌든 오타의 구렁텅이에서 벗어날 수 없는 개발자의 숙명에서 해방감을 느낄 수 있도록 해주는 친구다. 즉, 값을 잘못 입력할 수도 있는 실수를 방지해주는 역할을 한다.

enum Team { blue, red }

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

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

void main() {
  var player = Player(name: 'gigi', xp: 100, team: Team.blue, age: 99);
}

Abstract Classes

= 추상화 클래스

추상화 클래스로는 객체를 생성할 수 없고, 다른 클래스들이 어떤 형태의 메서드를 구현해야 하는지 정의하는 클래스라고 말할 수 있다. (메서드명과 반환 타입만 정의 가능한 우리 Abstract Class...) 추상화 클래스는 다른 클래스에서 상속받을 수 있고, 상속받은 클래스는 추상화 클래스의 메서드를 구체화 한다.

extends를 사용해서 추상화 클래스를 상속받을 수 있다.

abstract class Human {
  void walk();
}

class Player extends Human {
  void walk() {
    print("walk walk walk");
  }
}

void main() {
  Player().walk();
}

Inheritance

= 상속

부모 클래스 생성자에 파라미터가 있으면 자식 클래스 에서 직접 구현하여 값을 할당해주어야 한다. 즉, 자식 클래스에서 생성자를 생성할 때 부모 클래스의 파라미터를 추가한 뒤 super를 사용하여 부모 클래스에게 값을 전달한다. 아래 코드를 보면 쉽게 이해할 수 있을 것이다.

super부모 클래스 를 의미한다. 또한 @override를 사용하여 부모 클래스 의 메서드를 오버라이드 할 수 있다.

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

class Player extends Human {
  final String team;
  Player({required this.team, required String name}) : super(name: name);
  
  void sayHello() {
    super.sayHello();
    print("and I play for ${team}");
  }
}

void main() {
  var player = Player(team: "blue", name: "gigi");
  player.sayHello();
}

Mixins

생성자가 없는 클래스를 의미한다. 프로퍼티나 메서드를 추가할 때 사용하며, with을 사용하여 mixin 클래스에 접근할 수 있다. mixin 클래스는 여러 클래스에서 재사용 이 가능한 장점을 가지고 있다.

mixin Strong {
  final double strenghtLevel = 1500.99;
}

mixin Tall {
  final double height = 1.99;
}

class Player with Strong, Tall {
  Player();
}

void main() {
  var player = Player();
  print(player.strenghtLevel);
  print(player.height);
}
profile
Hello, World!

0개의 댓글

관련 채용 정보