- 객체지향
- 구글
- User Interface 만드는 데에 최적화
- 모든 플랫폼에서 빠르다
- 많은 플랫폼에 컴파일가능
0. INTRODUCTION
0-1. Why Dart
두 개의 컴파일러 (DART WEB/NATIVE)
- WEB: dart 코드 > javascript 변환 컴파일러
- NATIVE: dart 코드 > 여러 cpu 아키텍쳐에 맞게 변환 컴파일러 (ARM32, ARM64, 데스크탑 등)
사물인터넷에도 사용 가능
어떻게 컴파일 되는가 JIT / AOT(: c, c+, rust, go)
c++ 프로그램 만들었다 > win에 배포하고싶다 > win 바이너리로 컴파일해서 바이너리 제공해야 함
UI 만들다가 버튼 변경 > 다시 컴파일? 화면에서 바뀐 걸 바로 봐야하는데 전체 프로젝트가 컴파일 되면 좋은 개발 경험X, JIT 컴파일이 필요하다
dartVM사용, 바로 화면에 보여줌. 가상머신에서 작동하고 있다는 거라서 조금 느리지만 오직 개발중일때만. >> During development << dart 가상머신이 jit 컴파일러 제공 with 풍부한 디버깅 자원
앱을 배포하고 싶다면 dart VM이 아닌 aot 컴파일러인 것!
⇒ 개발도중: dart vm에서 실행(jit) 느리지만 바로바로 피드백
⇒ 배포: dart야 aot로 컴파일해줘 → 기계로 변환 → 앱 빨리 돌아감
null safety
안정된 프로그램
why flutter - dart?
- jit/aot 둘 다 있기 때문 모바일 개발에 아 주 좋은 언어라는 것. 빠른 피드백을 원하면서도 빠른 실행
- dart/flutter 모두 구글이 만들어 최적화 가능 = 다른 프레임워크는 거의 불가능 flutter를 위해 dart 언어를 최적화. 실례로 원래 DART에도 native 바이너리 지원하는 aot 툴체인 없었음. 추후에 dart에 추가됨. flutter 더 빠른 실행을 위해!
0-2. How to learn
설치 없이 dart 연습하기: dartpad.dev ⇒ dart 에디터
documentation 패널도 열리고, 에러도 보임, 실행도 가능
이미 dart/flutter가 있다면: 프로젝트 하나 파서 main.dart, vs코드는 익스텐션 깔기
잼있겠땅~
1. VARIABLES
1-0. Hello World
핵심: 모든 코드는 main 함수 안에 들어가야 한다!
main함수는 모든 dart 프로그램의 entry point
세미콜론은 아주 중요하다! 없으면 오류가 난다.
js/ts 등은 auto formatter가 달아주지만 dart는 아니다.
왜일까
세미콜론을 일부러 안 쓸 때가 있기 때문인데 나중에 배운다 [Cascade Operator]
void main() {
print('hello world');
}
1-1. The Var Keyword
**핵심:
- 변수를 선언할 때 var를 사용할 수도, 타입을 지정할 수도 있다.
- 타입만 유지된다면 변수는 업데이트가 가능하다.**
void main() {
var name = 'sori'; // (O) String 추측
name = 1; // (X) String <> int
name = true; // (X) String <> bool
}
void main() {
String name = 'sori'; // (O) Type 지정도 가능
}
var, type 명시 각각 언제 사용해야 할까?
- var 관습적으로 함수나 메서드 내부에 지역변수를 선언할 때
- type 명시 class에서 변수나 property를 선언할 때
⇒ dart 스타일 가이드에 따르면, var를 가능한 많이 사용하는 게 권장되고, 타입을 지정하는 방식은 class property를 작성할 때 권장된다.
1-2. Dynamic Type
핵심: dynamic = 여러가지 타입을 가질 수 있는 변수에 사용하는 키워드(어쩔 수 없을 때) 이상적으로는 쓰는 걸 피해야 하는 타입
void main() {
dynamic name; // var name도 되지만 dynamic이라고 명시해줄 수 있다.
name = 'sori'; (O)
name = 12; (O)
name = true; (O)
}
변수가 어떤 타입인지 알기 어려운 경우, 특히 flutter, json과 작업하면 유용한 경우가 있다.
위 코드블록에서 name. 해보면(자동완성 기능)
dart는 어느 타입인지 모르기 때문에 메서드 추천을 해주지 않는다.
하지만 타입이 확인되는 순간,
if (name is String) {
name. >> String 옵션들이 나타남 (contains, replaceAll ...)
}
1-3. Nullable Variables
핵심: Null Safety = 어떤 변수/데이터가 null이 될 수 있음을 명시하는 것
⇒ 어떤 데이터가 null일 때 참조하지 않도록 dart가 우릴 도와줘~
기본적으로 모든 변수는 non-nullable → ?를 붙이면 nullable
1. **null safety**하지 않은 코드
bool isEmpty(String str) => str.length == 0;
main() {
isEmpty(null);
}
// => Runtime Error (NoSuchMethodError)
// str이 null이다 -> length라는 메서드 찾을 수 없는 것, Runtime 오류!
2. **null safety**
void main() {
String example = 'ex';
example = null; (X)
// null도 될 수 있다는 것을 **'?'**로 명시해주자
String**?** example = 'ex';
example = null; (O)
example.isNotEmpty; (X) => Complie 단계에서 에러 잡아준다. might be null
2-1)
if (example!= null) { // 이제 바로 String이라는 것을 안다
example.isNotEmpty; (O)
}
2-2) // 단축문법
example?.isNotEmpty; (O)
}
개발자가 null값을 참조할 수 없도록 한다. ⇒ 컴파일 시에 잡아내면서 런타임 에러 방지!
이또한 dart의 초기 버전부터 있었던 기능은 아니다.
1-4. Final Variables
핵심: 한 번 정의된 변수를 수정할 수 없게 만들려면 final 변수를 만든다.
void main() {
final name = 'sori';
name = 'sory'; (X)
}
JS/TS의 const와 같다.
꼭 그럴 필요는 없지만 타입을 명시해줄 수도 있다. ex) final String
┗>어차피 바뀔 일 없기 때문일까??
1-5. Late Variables
핵심: final이나 var 앞에 붙여줄 수 있는 수식어로, 초기 데이터 없이 변수를 선언할 수 있게 해준다.
void main() {
late final String name;
// Do something Maybe go to api;
name = 'sori'; // └> 데이터를 받아서 넣어주겠지
name = 'yesori'; (X) final은 수정 불가.
}
값을 넣기 전에는 접근하지 말아야 한다. 예를 들어
void main() {
late final str;
print(str); (X)
// => 'The late local variable 'str' is definitely unassigned at this point.'
}
flutter로 data fetching 할 때 아주 유용하다. ↓
Create something (late variable)
⇒ Going to hit the API
⇒ When the API comes back
⇒ put the data on the late variable
1-6. Constant Variables
핵심: dart의 const는 compile-time constant를 만들어준다.
⇒ const는 compile-time에 알고 있어야 하는 것!!
void main() {
const name = 'sori';
name = 'yesori'; (X)
}
dart의 const <> JS/TS의 const ⇒ 이건 dart의 final과 비슷함. 오호~
수정이 안 된다 = 상수이다. 이긴 하지만 API/화면 입력 등에서 정해지는 값이 아니라,
앱스토어에 올리기 전 이미 알고 있는 value인 것
void main() {
const API = 'a123ads134'; // compile-time constant (O)
const API = fetchApi()~ // compile-time constant (X)
└> final로 사용하는 게 맞다.
}
2. DATA TYPES
2-1. Basic Data Types
String name = 'a'; // "a"
bool arive = true;
int age = 1;
double weight = 1.25;
대부분의 자료형은 Object이다.
이 중, int와 double은 extends num
num x = 1;
num y = 1.1;
2-2. Lists
void main() {
var numbers = [1, 2, 3, 4];
List<int> numbers = [1, 2, 3, 4]; // same Effect
// 참고: 맨 끝에 ,를 붙이면 자동으로 formatting 됨
=> List<int> numbers = [
1,
2,
3,
4,
]
numbers.add('a'); (X)
numbers.add(1); (O)
}
first/last, add, addAll, clear, contains, (…)
dart에서의 멋진 점: collection if / collection for를 지원한다.
🧡 collection if에 대해 간략히:
List를 만들 수 있는데, 존재할 수도 안 할 수도 있는 요소를 가지고 만들 수 있다. WHAT?
↓ example
void main() {
var giveMeFive = true;
var numbers = [1, 2, 3, 4, if (giveMeFive) 5,];
// 만약 giveMeFive가 true => List에 5 추가
print(numbers); => [1, 2, 3, 4, 5]
}
2-2. String Interpolation
핵심:
String Interpolation = text에 $변수를 추가하는 방법
void main() {
var name = 'sori';
var greeting = 'Hello, my name is $name, nice to meet you!';
print(greeting); => Hello, my name is sori, nice to meet you!
========================================================================
var name = 'sori';
var age= 10;
var greeting = 'Hello, my name is $name, I\\'m ${age + 1}.'; //escape!
print(greeting); => Hello, my name is sori, I'm 11.
}
single/double quote 상관 없고, $ 뒤 변수를 사용해주기만 하면 끝
단순 변수 값을 넣고 싶은 경우 ⇒ $변수명
무언가를 계산하고 싶은 거라면 ⇒ ${변수명 with 연산}
변수가 이미 존재할 때 사용하는 방식
2-3. Collection For
핵심:
향상된 for문 같지만 내부 블록 없이 List 안에서 바로 사용 가능!!
void main() {
var oldFriends = ['은별', '연주'];
var newFriends = [
'왕자',
'공주',
for (var friend in oldFriends) "💖$friend",
];
print(newFriends); => [왕자, 공주, 💖은별, 💖연주]
}
2-4. Maps
JS/TS의 object, Python의 dictionary 같은 것
void main() {
var player = { // 컴파일러가 정한 자료형은: Map<String, Object>
'name': 'sori',
'xp': 19.99,
'superpower': false,
};
Map<int, bool> player2 = { // 직접 타입 지정할 때
0: false,
1: false,
2: false,
};
Map<List<int>, bool> player2 = { // 복잡한 타입의 key
[0]: false,
[1, 3]: false,
[2, 4, 5]: false,
};
List<Map<String,Objet>> players = { // List와 혼합
{'name': 'sori', 'xp': 19.99, 'superpower': false,},
{'name': 'soru', 'xp': 23.99, 'superpower': true,},
}
// 하지만 이와 같이 API 구조 등과 같은 데이터(특정 형태)라면
// Map보다 class 사용을 권장하지!
}
2-5. Sets
void main() {
var numbers = {1, 2, 3, 4,};
Set<int> numbers = {1, 2, 3, 4};
numbers.add(1);
numbers.add(1);
numbers.add(2);
print(numbers) => {1, 2, 3, 4};
}
모든 값이 Unique하고, 순서 존재
Dart List = Python List | Dart Set = Python Tuple
3. FUNCTIONS
3-0. Defining a Function
void sayHello(String name) {
print("Hello $name nice to meet you!");
}
void main() {
sayHello('sori');
}
곧바로 return되는 함수를 정의할 때
String sayHello(String name) {
return "Hello $name nice to meet you!";
}
를 Fat Arrow Syntax를 사용하여 바꿀 수 있다.
String sayHello(String name) => "Hello $name nice to meet you!";
함수가 복잡한 경우에는 물론 아래와 같이 정석대로 정의
String sayHello(String name) {
// Call API
// Perform calculate
// ...
return "...";
}
+ num 타입 추가
num plus(num x, num y) => x + y;
(arguments) 받아서 => 곧바로 return 하는 것과 같은 의미.
코드가 한 줄밖에 없을 때, 위와 같이 사용하면 된다.
3-1. Named Parameters
Dart의 Function은 Named Parameter를 지원한다.
String sayHello(String name, int age, String country) {
return "Hello $name, you are $age, and you come from $country!";
}
void main() {
print(sayHello('sori', 20, 'korea'));
}
Positional Parameter, 위 코드의 문제점:
요소의 순서를 잊을 수 있고, 가독성이 좋지 않다(파라미터의 의미 알 수 없어)
두 개 정도까지는 괜찮지만 세 개부터 복잡해지는,, 클린 코드 뭐시기법 위반!
⇒ Named Parameter를 사용해보기
호출 시에는 순서와 관계 없이 argument의 이름(name, age, country)들을 적어준다.
String sayHello({
String name,
int age,
String country}) { // 중괄호 추가!
return "Hello $name, you are $age, and you come from $country!";
}
void main() {
print(sayHello(
age: 20,
country: 'korea',
name: 'sori'
));
}
하지만 이 상태라면 null safety로 인해 컴파일 오류가 뜬다.
해결하기 위한 방법 두 가지
- default값 지정
String sayHello({
String name = 'anonymous',
int age = 99,
String country = 'wakanda'}) {
return "Hello $name, you are $age, and you come from $country!";
}
void main() {
print(sayHello());
// => Hello anonymous, you are 99, and you come from wakanda!
}
- ‘required’ modifier사용
String sayHello({
required String name,
required int age,
required String country}) {
return "Hello $name, you are $age, and you come from $country!";
}
void main() {
print(sayHello()); // !!Compile Error!!
}
실험해봤는데 Positional Parameter과 required는 함께 사용 불가하다. (그러고보니 이미 required…)
3-2. Optional Positional Parameters
Named Parameter는 적용하고 싶지 않은데, country 는 required하지 않게 하고싶다면?
String sayHello(String name, int age, [String? country = 'wakanda']) =>
return "Hello $name, you are $age, and you come from $country!";
void main() {
print(sayHello('sori', 20)); // => Hello sori, you are 20, and you come from wakanda!
}
[ ] 와 ?, default값 지정을 통해 걸러낼 수 있다.
3-3. QQ Operator(Question Question Operator)
핵심:
‘??’ | 좌항 ?? 우항 ⇒ 좌항이 null이면 우항을 return
‘??=’ | 좌항이 null 이면 우항을 대입
🧡 이름을 대문자로 리턴하는 함수를 만들어보자.
String capitalizeName(String name) => name.toUpperCase();
사용자가 null도 입력할 수 있으면 좋겠다. 그래서 파라미터를 String? name 으로 바꿔준다.
String capitalizeName(String? name) => name.toUpperCase();
‼하지만 이러면 toUpperCase를 사용할 수 없다.
해결방안은 세 가지가 있다.
- name에 대해 null check 수행
String capitalizeName(String? name) {
if (name != null) {
return name.toUpperCase();
}
return 'ANONYMOUS';
}
- 삼항 연산자 사용
String capitalizeName(String? name) =>
name != null ? name.toUpperCase() : 'ANONYMOUS';
- QQ 사용
String capitalizeName(String? name) =>
name?.toUpperCase() ?? 'ANONYMOUS';
💚 QQ equals (혹은 QQ asignment operator) 알아보기
⇒ null이라면 값을 할당해준다.
void main() {
String? name;
name ??= 'sori';
name = null;
name ??= 'yesori';
print(name) => // yesori
String? name;
name ??= 'sori';
name ??= 'yesori'; // 해당 줄은 실행될 일 NEVER
print(name) => // sori
}
3-4. Typedef
자료형이 헷갈릴 때 도움이 될 alias를 만드는 방법
긴 코드를 만났을 때 유용할 것!
🧡 1. 숫자로 된 List를 반대로 뒤집어서 return 하는 함수를 만들어보자.
List<int> reverseListOfNumbers(List<int> list) {
var reversed = list.reversed; // => List와는 조금 다른 iterable이 되어 toList() 필요
return reversed.toList();
}
- List<int>에 대한 alias를 지정해보자.
typedef ListOfInts = List<int> // ‼
ListOfInts reverseListOfNumbers(ListOfInts list) {
var reversed = list.reversed;
return reversed.toList();
}
원하는 만큼의 typedef를 생성할 수 있다.
💚 Map도 만들어보자. 사용자에게 인사하는 함수를 Map type의 alias와 함께 만들어보자.
typedef UserInfo = Map<String, String>;
String sayHi(UserInfo userInfo) { // == String sayHi(Map<String, String> userInfo) {
return "Hi ${userInfo['name']}";
}
이전에도 정리했지만 구조화된 data 형태를 지정하고 싶다면 class를 사용하는 것이 좋다!
4. CLASSES 💫
4-0. Your First Dart Class
- class 작성해보기
✅ class에서 property 선언할 때는 clear하게 타입 정의하기
class Player {
String name = 'sori';
int xp = 1500;
}
- Player class의 Instance 생성해보기(Object)
void main() {
var player = Player(); // **‘new’ 키워드는 쓸 수는 있지만 쓸 필요 없다.**
// Do Something with player ...
}
- variables modifier에 대해 배운 것을 토대로 class에 적용해보자
class Player {
final String name = 'sori';
int xp = 1500;
}
void main() {
var player = Player();
player.name = 'ddori'; (X) // final 변수 변경 불가
}
- player가 인사하는 메서드를 생성해보자.
class Player {
final String name = 'sori';
int xp = 1500;
void sayHello() {
print("Hello my name is $name"); **// this를 쓸 수는 있지만 쓸 필요 없다.**
}
}
void main() {
var player = Player();
}
✅ Class method에서의 this는 사용하지 않도록 권고되어있다. variable과 class property의 이름이 겹치는 게 아니라면… ↓example
class Player {
final String name = 'sori';
int xp = 1500;
void sayHello() {
var name = 'example';
print("Hello my name is ${this.name}");
// 이 때 player의 이름에 접근하고 싶다면 this를 사용한다.
}
}
4-1. Constructors
class Player {
**late** final String name;
**late** int xp;
Player(String name, int xp) {
this.name = name; // final 변수라서 late를 붙이지 않으면 안 됨! Advantage of 'late'
this.xp = xp;
}
}
void main() {
var player = Player('sori', 1500);
var player = Player('ddori', 1800);
}
Java 문법이랑 아주 유사하다.
하지만위 코드에서 Dart는 이미 name과 xp의 타입을 알고 있다. 너무 반복적인 작업을 하고 있다.
🧡 Constructor 코드를 작성하는 더 나은 방법 How cool is that~
- late 지워
- this.property = argument 할당 지워
- 들어온 parameter가 어디에 할당될지 바로 명시 ↓example
class Player {
final String name;
int xp;
Player(this.name, this.xp);
}
Beautiful!
4-2. Named Constructor Parameters
4-1에서 작성한 Constructor를 통한 객체 생성 ⇒ Using Positional Parameters (depends on the position)
class Player {
final String name;
int xp;
+) 추가
String team;
int age;
Player(this.name, this.xp, this.team, this.age);
}
void main() {
var player = Player('sori', 1500, 'red', 20);
// ⇒ 생성자 호출 시 도무지 의미를 알 수 없는 parameter들!!
}
🧡 Let’s Use Named Constructor Parameters
class Player {
final String name;
int xp;
String team;
int age;
Player({this.name, this.xp, this.team, this.age}); // **{ }로 감싸기**
// 하지만 이렇게 되면 위 argument들은 might be null >> ERROR!
}
void main() {
var player = Player(
name: 'sori',
xp: 1500,
team: 'red',
age: 20
);
}
⇒ Looks much much better **BUT**
null safety에 걸리는 생성자 argument들을 어떻게 잡아줘야 할까? (복습)
- default
- required ✔
class Player {
final String name;
int xp;
String team;
int age;
Player({
required this.name,
required this.xp,
required this.team,
required this.age
}); // 💖 HOW NICE IS THIS!
}
void main() {
var player = Player(
name: 'sori',
xp: 1500,
team: 'red',
age: 20
);
}
4-3. Named Constructors
Flutter에서 constructor를 만들 때 많이 사용하는 패턴 중 하나
🧡 Named Constructor 예시
- 객체가 생성되었을 때 xp는 기본 1500, 팀은 각각 blue/red로 정해주고 싶다.
- 이름과 나이는 입력받는다.
class Player {
final String name;
String team; // 이것두 합치면 둘 다 final이 되니 주의
int xp, age; // 한 번에 선언 가능
Player({
required this.name,
required this.xp,
required this.team,
required this.age
});
// [1] BLUE: Named Constructor with Named Syntax Parameter
Player.createBluePlayer({ // 1. 생성자 추가
required this.name, // 2. 입력 부분은 동일
required this.age,
}) **:** this.age = age, // 3. 콜론 뒤에서 초기화 해주기
this.name = name,
this.team = 'blue',
this.xp = 1500;
↑ ❗ ❗ ❗
// [2] RED: Named Constructor with Positional Syntax Parameter
Player.createRedPlayer(String name, int age)
**:** this.age = age,
this.name = name,
this.team = 'red',
this.xp = 1500;
// [3] 댓글에서 읽었음 !
Player.createWhitePlayer(
this.name,
this.age,
[this.team = 'white', this.xp = 1500]
);
}
void main() {
var bluePlayer = Player.createBluePlayer(name: 'sori', age: 20); // Named
var redPlayer = Player.createRedPlayer('ddori', 21); // Positional
var whitePlayer = Player.createWhitePlayer('ddoddoriri', 30);
}
- 생성자 추가: 기본 생성자 Player() 외에 또다른 생성자를 추가한다. 이름은 각각 Player.createBluePlayer( ) / Player.createRedPlayer( )
- 입력 부분은 동일: 파라미터를 입력받는 부분은 동일 (name, age)
- 콜론 뒤에서 초기화 해주기: 기본값 지정을 위해서는 특이한 문법이 사용된다. Player.createBluePlayer({ 입력 파라미터 }) : this. … (위 코드블럭 참조)
4-4. Recap
우리가 맞닥뜨릴 일.
🧡 API로부터 받은 데이터(Maybe Json format data)를 받아서, Dart Class로 바꾸기
- Json Data를 받아왔다고 가정해보자: var apiData [{…}]
class Player {
final String name;
String team;
int xp;
}
void main() {
var apiData = [
{
"name": "sori",
"team": "blue",
"xp": 1000,
},
{
"name": "ddori",
"team": "blue",
"xp": 1000,
},
{
"name": "yesori",
"team": "blue",
"xp": 1000,
},
];
}
- ‘fromJson’이라는 Named Constructor 생성 많은 Flutter 앱이 사용하는 패턴이다. 사용해보자.
class Player {
final String name;
String team;
int xp;
Player.fromJson(Map<String, dynamic> playerJson) :
name = playerJson['name'],
team= playerJson['team'],
xp = playerJson['xp'];
void sayHello() {
print('Hello! my name is $name');
}
}
- 2번에서 만든 생성자로 데이터를 확인해보기
void main() {
var apiData = [
{
"name": "sori",
"team": "blue",
"xp": 1000,
},
{
"name": "ddori",
"team": "blue",
"xp": 1000,
},
{
"name": "yesori",
"team": "blue",
"xp": 1000,
},
];
apiData.forEach((playerJson) {
var player = Player.fromJson(playerJson);
player.sayHello();
});
}
/*
main 실행 시
=> Hello! my name is sori
Hello! my name is ddori
Hello! my name is yesori
*/
4-5. Cascade Notation
다시 기본적인 Named Syntax로 작성했던 코드로 진행한다.
class Player {
String name;
String team;
int xp;
Player({
required this.name,
required this.team,
required this.xp,
});
void sayHello() {
print('Hello! my name is $name');
}
}
void main () {
var sori = Player(name: 'sori', team: 'blue', xp: 1200);
sori.name = 'yesori';
sori.xp = 12000;
sori.team = 'white';
}
위 코드에서 sori. sori. sori. 가 계속 반복된다 …
syntax sugar를 뿌려보자🍭
void main () {
var sori = Player(name: 'sori', team: 'blue', xp: 1200) // 세미콜론 떼고!
..name = 'yesori'
..xp = 12000
..team = 'white';
} **MUCH LESS CODE!**
void main () {
var sori = Player(name: 'sori', team: 'blue', xp: 1200);
var potato = sori
..name = 'yesori'
..xp = 12000
..team = 'white';
print(sori.name); // => yesori
print(potato.name); // => yesori 이해가 잘 ... 깊은 복사?
}
4-6. Enums
Enum은 텍스트 형태로 작성하지 않는다.
enum Team { red, blue }
enum XPLevel { beginner, medium, pro }
class Player {
String name;
Team team;
XPLevel xp;
Player({
required this.name,
required this.team,
required this.xp,
});
}
void main () {
var sori = Player(name: 'sori', team: Team.blue, XPLevel : XPLevel.beginner);
}
4-7. Abstract Classes
추상화 클래스 = 객체 생성 불가, blueprint
extends
abstract class Human {
void walk();
}
enum Team { red, blue }
enum XPLevel { beginner, medium, pro }
class Player extends Human {
// 생략 ...
**void walk() {**
print('I\\'m walking');
}
}
class Coach extends Human {
**void walk() {**
print('The coach is walking');
}
}
void main () {
}
4-8. Inheritance
상속 ⇒ 부모 생성자 호출 필요, 얘도 : 문법 사용
메서드 Overriding 가능: 부모 메서드는 호출 하든말든 상관 X
class Human {
final String name;
Human({required String this.name});
// Human(this.name);
void sayHello() {
print('Hello my name is $name.');
}
}
enum Team {blue, red}
class Player extends Human {
final Team team;
Player({
required this.team,
required String name // <- 얘를 Human 생성자로 전달해줘야함.
}) : **super(name: name)**; // 맨 위 주석 해제 시 => super(name); (Positional)
@override
void sayHello() {
super.sayHello();
print('and I play for ${team}.');
}
}
void main () {
var player = Player(name: 'sori', team: Team.blue);
player.sayHello();
}
4-9. Mixins
생성자가 없는 클래스
상속은 부모클래스를 확장하고, super를 통해 부모에 접근하며, 부모 클래스의 인스턴스가 되지만,
Mixin은 with 키워드를 통해 단순히 내부의 속성과 메서드를 뺏어온다. Super Super Useful
- 여기 Player라는 클래스가 있다.
enum Team {blue, red}
class Player {
final Team team;
Player({
required this.team,
});
}
- Mixin 클래스를 세 개 만들어본다.
- ** 버전 상승에 따라 class Strong (X) ⇒ mixin Strong || mixin class Strong (O)
mixin Strong {
final double strengthLv = 1399;
}
mixin QuickRunner {
void runQuick() {
print('ruuuuun~~~!');
}
}
mixin class Tall {
final double height = 189;
}
- Mixin 클래스를 활용해본다. ‘with’
mixin Strong {
final double strengthLv = 1399;
}
mixin QuickRunner {
void runQuick() {
print('ruuuuun~~~!');
}
}
mixin class Tall {
final double height = 189;
}
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.blue);
player.runQuick();
}
⇒ 다른 클래스의 property, method를 그냥 긁어오는 것으로, 상속과는 다르다.
댓글