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();
}
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;
이 된다.
인스턴스를 생성할 때 파라미터의 개수가 늘어날수록 "어떤 파라미터가 어떤 값을 가져야 하고 얜 뭐고 ···" 하는 상황을 방지하기 위한, 직관성을 향상시킬 수 있는 방법이다. 함수를 생성할 때도 이렇게 파라미터의 이름을 명시할 수 있도록 하는 방법이 있었는데, 생성자를 생성할 때도 동일하다.
역시나 중괄호({})
생성자의 요소들을 감싸고, 인스턴스 생성 시 파라미터의 이름을 명시하여 값을 던져주면 된다. 이러한 방식은 기본적으로 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');
}
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이다.
이것 또한 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;
}
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 Class...) 추상화 클래스는 다른 클래스에서 상속받을 수 있고, 상속받은 클래스는 추상화 클래스의 메서드를 구체화 한다.
extends
를 사용해서 추상화 클래스를 상속받을 수 있다.
abstract class Human {
void walk();
}
class Player extends Human {
void walk() {
print("walk walk walk");
}
}
void main() {
Player().walk();
}
= 상속
부모 클래스 생성자에 파라미터가 있으면 자식 클래스 에서 직접 구현하여 값을 할당해주어야 한다. 즉, 자식 클래스에서 생성자를 생성할 때 부모 클래스의 파라미터를 추가한 뒤 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();
}
생성자가 없는 클래스를 의미한다. 프로퍼티나 메서드를 추가할 때 사용하며, 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);
}