#4.0 Your First Dart Class
flutter에서는 class가 많이 쓰이기 때문에 중요하다.
class에서 property를 선언할때는 타입을 사용해서 정의한다.
class Player {
String name = 'dart';
int xp = 1500;
}
void main(){
var player = Player();
print(player.name);
player.name = 'awdawd';
print(player.name);
}
다음과 같은 코드처럼 property의 값을 바꿀 수도 있고, 가져오기만 할 수도 있다.

예를 들어 Player의 name을 바꾸지 못하게 하고 싶다면?
class Player {
final String name = 'dart';
int xp = 1500;
void sayHello(){
print("Hi my name is $name");
}
}
void main(){
var player = Player();
player.sayHello();
}
String 앞에 final을 추가하기만 해주면 된다. 이 경우에는 name을 바꿀 수 없기 때문에 player.name = 'awdawd'; 는 오류가 나게된다.
class Player {
final String name = 'dart';
int xp = 1500;
void sayHello(){
var name = '123';
print("Hi my name is ${this.name}");
}
}
다음과 같이 method 내에서 같은 이름의 variable가 있을때 Player의 name에 접근 하기 위해서는 this.name을 써주어야 한다. 만약 아니라면 굳이 사용할 필요가 없다.
#4.1 Constructors
class Player {
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("dart",1500);
player.sayHello();
var player2 = Player("lynn",2500);
player2.sayHello();
}
class에 parameter(argument)를 넘겨주면 위쪽에 있는 Player()에서 parameter을 받아 정해진 자리에 할당한다.
constructor 코드를 작성하는 더 나은 방법은, 일단 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("dart",1500);
player.sayHello();
var player2 = Player("lynn",2500);
player2.sayHello();
}
위에서 이미 type을 선언해주었기 때문에, Player(this.name, this xp) 만으로도 값을 불러올 수 있다.
#4.2 Named Constructor Parameters
#4.1에서 위치에 따라 달라지는 positional arguments를 사용하고 있다. (첫 번째는 name, 두 번째는 xp)
예를 들어 team과 age라는 정보를 추가하여 코드를 작성하면
class Player {
final String name;
int xp;
String team;
int age;
Player(this.name, this.xp, this.team, this.age){
}
void sayHello(){
print("Hi my name is $name");
}
}
void main(){
var player = Player("dart",1500, 'red', 12);
player.sayHello();
var player2 = Player("lynn",2500, 'blue', 12);
player2.sayHello();
}
입력받아야 하는 값이 너무 많아지기 때문에 헷갈리기 쉽다. 그렇다면 이를 named constructor parameters 로 변경하려면 아래와 같이 변경해주면 된다.
class Player {
final String name;
int xp;
String team;
int age;
Player({
required this.name,
required this.xp,
required this.team,
required this.age
}){
}
void sayHello(){
print("Hi my name is $name");
}
}
void main(){
var player = Player(
name: "dart",
xp: 1500,
team: 'red',
age: 12,
);
player.sayHello();
var player2 = Player(
name: "lynn",
xp: 2500,
team: 'blue',
age: 12,
);
player2.sayHello();
}
Player()에 중괄호를 추가해주고, 아래의 var값을 다음과 같이 변경하면 되는데, 값이 null인 경우를 dart는 걱정하기 때문에 앞에 required를 추가 해주면 된다.
#4.3 Named Constructors
조금 다르게 적용하는 constructor
하나는 xp를 기본값 0으로 초기화 시킨 blue팀을 가진 Player를 만들고, 다른 하나는 xp를 0으로 초기화 시킨 red팀의 Player를 만들게 하고 싶다고 한다면...
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({required String name, required int age}) :
this.age = age,
this.name = name,
this. team = 'blue',
this.xp = 0;
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: "dart",
age: 12,
);
var redplayer = Player.createRedPlayer("dart", 12);
}
Player.createBluePlayer을 봤을 때 :(콜론) 뒤에 들어간 property들은 초기화 된다. 즉 team, xp는 설정한 'blue', 0 으로 계속 초기화 되고 age와 name만 사용자에게 받아오게 된다.
#4.4 Recap
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": "dart",
"team": "red",
"xp": 0,
},
{
"name": "lynn",
"team": "red",
"xp": 0,
},
{
"name": "dat",
"team": "red",
"xp": 0,
}
];
apiData.forEach((playerJson){
var player = Player.fromJson(playerJson);
player.sayHello();
});
}
구조화되지 않은 데이터를 가져올 수 있다. string을 key로 dynamic value를 values로 갖는 Map을 가져오면 다음과 같은 결과가 나온다.

#4.5 Cascade Notation
class Player {
String name;
int xp;
String team;
Player({required this.name, required this.xp, required this.team});
void sayHello(){
print("Hi my name is $name");
}
}
void main(){
var nic = Player(name: 'nic', xp: 1200, team: 'red');
nic.name = 'las';
nic.xp = 120000;
nic.team = 'blue';
}
nic이라는 Player가 있을 때 값을 바꿔주는 경우 아래와 같이 nic.name 형식을 이용하여 변경하게 된다.
하지만 이는 nic.xxx를 반복하기 때문에 이를 줄이도록 Cascade operator을 사용 할 수 있다.
void main(){
var nic = Player(name: 'nic', xp: 1200, team: 'red')
..name = 'las'
..xp = 120000
..team = 'blue';
}
방법은 먼저 세미콜론(;)을 지우고 nic 대신 . 을 하나 더 입력하면 전과 같은 기능을 한다.
void main(){
var nic = Player(name: 'nic', xp: 1200, team: 'red')
var potato = nic
..name = 'las'
..xp = 120000
..team = 'blue'
..sayHello();
}
앞에 class가 있다면 아래의 코드들은 바로 class를 가리키게 된다.
#4.6 Enums
Enum은 실수를 줄여주는 역할을 한다.
예를 들어 팀 색깔을 나타낼때 blue를 bule라고 쓴다거나 flex를 felx라고 쓰는 등 실수하기가 쉽다. 이 경우 선택의 폭을 줄여는 Enums를 이용하여 실수를 줄일 수 있다.
enum Team {red, blue}
enum XPLevel {beginner, medium, pro}
class Player {
String name;
XPLevel xp;
Team team;
Player({required this.name, required this.xp, required this.team});
void sayHello(){
print("Hi my name is $name");
}
}
void main(){
var nic = Player(name: 'nic', xp : XPLevel.medium , team: Team.red);
var potato = nic
..name = 'las'
..xp = XPLevel.pro
..team = Team.blue
..sayHello();
}
enum 을 이용하여 어떤 값이 들어갈 수 있는지 먼저 정의해주고, 사용할때는 더 이상 직접 값을 넣어주는 것이 아닌 enum을 통해서 값을 넣어줄 수 있다. 예를들어 team : 'red' 는 team : Team.red 로 표현이 가능하다.
#4.7 Abstract Classes (추상화 메소드)
abstract class(추상화 클래스)로는 객체를 생성할 수 없다. 추상화 클래스는 다른 클래스들이 직접 구현 해야하는 메소드들을 모아놓은 청사진(blueprint)이라고 볼 수 있다.
예를 들어 Human이라는 추상적 메소드를 만들고 walk이라는 메소드를 가질 때 이 메소드의 시그니쳐가 무엇인지 정의한다. 이 메소드가 반환하는 값이 무엇인지 확인하는 것이다. 여기서는 void가 될 것이고,
abstract class Human {
void walk();
}
enum Team {red, blue}
enum XPLevel {beginner, medium, pro}
class Player extends Human{
String name;
XPLevel xp;
Team team;
Player({required this.name, required this.xp, required this.team});
void sayHello(){
print("Hi my name is $name");
}
}
클래스의 형태를 사용하고 싶을 때는 class 옆에 extends 를 붙여 사용 할 수 있다. 하지만 이 경우에는 Player에서 오류가 나는데, 그 이유는 walk이라는 메소드가 없기 때문이다.
추상화 클래스는 메소드의 이름과 반환 타입만 정해서 정의가 가능하고, 파라미터도 성정이 가능하다.
abstract class Human {
void walk();
}
enum Team {red, blue}
enum XPLevel {beginner, medium, pro}
class Player extends Human{
String name;
XPLevel xp;
Team team;
Player({required this.name, required this.xp, required this.team});
void sayHello(){
print("Hi my name is $name");
}
void walk(){
print("i'm walkin");
}
}
class Coach extends Human{
void walk(){
print('the coach is walking');
}
}
void main(){
var nic = Player(name: 'nic', xp : XPLevel.medium , team: Team.red);
var potato = nic
..name = 'las'
..xp = XPLevel.pro
..team = Team.blue
..sayHello();
}
예를 들어 Coach라는 새로운 클래스를 만들 때 위 코드처럼 walk 메소드를 무조건 만들어주어야 한다.
#4.8 Inheritance
class Human {
final String name;
Human(this.name);
void sayHello(){
print("Hi my name is $name");
}
}
enum Team {blue, red}
class Player extends Human {
final Team team;
Player({
required this.team,
required String name
}) : super(name);
@override
void sayHello(){
super.sayHello();
print('and I play for ${team}');
}
}
void main(){
var player = Player(team : Team.red, name: 'nic');
player.sayHello();
}
@override 는 Human에서 온 sayHello를 직접 만든 메소드로 대체한다.
super은 확장(상속)한 부모 클래스의 property에 접근하게 하거나 메소드를 호출할 수 있게 해준다.
확장한 부모 클래스가 생성자를 포함하고 있는데, 그 클래스를 다른 곳에서 사용하려면 필요한 값을 전달해야 하고 부모 클래스의 생성자를 호출해주어야 한다.
#4.9 Mixin
Mixin은 생성자가 없는 클래스를 의미한다.
mixin Strong{
final double strengthLevel = 1500.99;
}
mixin QuickRunner{
void runQuick(){
print("ruuuuun");
}
}
mixin Tall {
final double height = 1.99;
}
enum Team {blue, red}
class Player with Strong, QuickRunner, Tall{
final Team team;
Player({
required this.team,
});
}
class Horse with Strong, QuickRunner {}
class Kid with QuickRunner {}
void main(){
var player = Player(
team: Team.red
);
}
다른 클래스의 preoperty와 메소드를 긁어오는 것 뿐이기 때문에 상속받을 필요가 없어서 편리하다.
Mixin의 조건은 생성자가 없는 클래스여야 한다.