Introduction
Dart is a language Optimized for UI
Dart has 2 compilers
- dart web: javascript로 변환
- dart native: ARM64, x86_64 등 여러 CPU 아키텍처에 맞게 변환
AOT(Ahead-Of-Time)
JIT(Just-In-Time)
Dart는 개발 환경에서 VM을 이용하여 JIT로 바로 실행 결과 볼 수 있게 함. 이때는 코드가 가상 머신에서 작동하기 때문에 느리다
배포 시에는 AOT를 이용하여 컴파일된 파일이 더 빠르게 작동할 수 있게 한다.
null safety: 안전한 프로그램 위해 반영됨 (null 참조 오류 방지)
Flutter가 Dart를 채택한 이유
- JIT와 AOT가 모두 제공됨
- Flutter와 Dart 모두 구글에서 만들기 때문에, 서로 필요 시에 수정이 가능함 (cf. 리액트를 위해 JS를 수정할 수는 없음)
Variables
1. Var
dart style guide에서는 var 사용을 권장한다. (지역 변수 등에는 var 사용-컴파일러는 String임을 알고 있음)
특정 자료형은 class property 작성할 때 유용하다.
void main() {
var name1 = 'EJ';
String name2 = 'NC';
name2 = 'NCLS';
// 값 바꿀 때는 자료형 같아야 함
// name1 = 97;
// name2 = 79;
}
2. Dynamic
타입 알기 전에 사용하면 좋다 (flutter, json에 유용)
이상적으로는 dynamic 쓰는 것을 피할 것
(var과의 차이점: var는 초기에 값을 지정하면 타입이 고정되어 변경 불가)
void main() {
var name1; // 처음 선언 시 초기화하지 않으면 dynamic으로 지정됨
name1 = 'EJ';
name1 = 97;
name1 = true;
dynamic name2;
if (name2 is String) {
// 이 안에서는 name2가 String인 것 알 수 있음
// name2. -> 그래서 더 다양한 메소드 조회 가능
}
var name3 = 'NC';
name3 = 79; // 에러 발생
dynamic name4 = 'JO';
name3 = 78; // 에러 발생하지 않음
}
3. null safety
컴파일 전에 null 참조 잡아내어 Runtime Error를 방지한다.
null이 될 수 있는 값 명시해줄 것
type 뒤에 ?을 붙여 사용한다 (e.g., String?)
기본적으로 모든 변수는 non-nullable
void main() {
String? ej = 'EJ';
ej = null;
ej.length;
// ej가 null일 수도 있기 때문에 에러
if (ej != null) {
ej.isNotEmpty; // 여기서는 사용 가능
}
ej?.isNotEmpty; // 이렇게 간단하게 체크할 수도 있음
}
4. final
이후에 값을 변경할 수 없는 변수 (재할당할 수 없는 변수)
void main() {
final name = 'EJ';
}
5. late
late: var / final 앞에 붙여줄 수 있음
초기 데이터 없이 변수 선언하게 해 준다
void main() {
late final String name;
print(name); // -> 아직 name 값이 없으니 작동할 수 없음
// 데이터 받아오기 등 작업
name = 'EJ';
print(name); // 이제 가능
}
6. const
const: compile-time constant
컴파일 하기 전에 답을 알 수 있는 경우에 사용
값 변경은 불가능
final은 런타임 중에 만들어질 수 있음
void main() {
const API = fetchApi(); // 이건 compile 시점에 알 수 없음
const API2 = 1123124; // 이건 가능
}
Data Types
7. List
void main() {
var nums1 = [
1,
2,
3,
4, // 마지막에 쉼표 넣으면 여러 줄로 포매팅 가능
];
List<int> nums2 = [1, 2, 3, 4];
nums2.add(5);
nums1.first; // 첫 번째 요소
// collection if : 존재할 수도, 안 할 수도 있는 변수로 리스트 생성 가능
var five = true;
var ifFive = [
1,
2,
3,
4,
if (five) 5, // five가 true인 경우 5가 추가됨
];
// collection for
var olds = ['N', 'B'];
var news = [
'J',
'K',
for (var o in olds) "👽$o",
];
print(news);
// 출력 결과: [J, K, 👽N, 👽B]
}
8. String Interpolation
$ 기호 뒤에 변수 사용하기 (변수가 이미 존재할 때)
계산할 때는 ${} 안에 변수 넣기
', " 모두 사용 가능
escape는 \
void main() {
var name = 'EJ';
var age = 22;
var greeting = 'Hi Hello, I\'m $name and I\'m ${age + 10}';
print(greeting);
}
9. Map
python의 dictionary와 유사
dart에서 object는 어떤 것도 될 수 있음 (typescript의 any와 유사)
API에서 값을 얻어 오는 경우에는 map 대신 class 권장
void main() {
// Type: Map<String, Object>
// dart에서는 컴파일러가 자동으로 타입 파악
var player1 = {
'name': 'EJ',
'xp': 97,
'power': false,
};
// 컴파일러 대신 직접 타입 설정할 수 있음
Map<int, bool> player2 = {
1: true,
// 2: 1; => 에러 발생
};
}
10. Set
set은 중복 허용하지 않음
python의 tuple과 유사
void main() {
var nums = {1, 2, 3, 4};
nums.add(1); // 변화 없음
}
Functions
11. Function
String sayHello(String name) {
return 'Hello $name';
}
// fat arrow syntax
// 즉시 리턴 등의 한 줄 코드에서 사용 가능
String sayHello2(String name) => 'Hello $name';
num plus(num a, num b) => a + b;
void main() {
print(sayHello("EJ"));
}
12. named parameters
named parameter를 지정하는 방법
1. default value를 미리 지정한다
2. required로 설정한다
String sayHello(
// 1. default value 지정
//{String name = 'johnDoe', int age = 100, String country = 'sunshineCity'}) {
// 2. required modifier
{required String name,
required int age,
required String country}) {
return 'Hello $name, you are $age years old, and you are from $country';
}
void main() {
// positional parameter의 단점: 파라미터의 의미 직관적으로 알기 어려움
// print(sayHello('EJ', 23, 'Korea'));
// 해결책: named arguments
// 순서 관계 없이 자료형 맞춰주면 사용 가능
print(sayHello(age: 19, country: 'taiwan', name: 'NC'));
}
13. optional positional parameter
String sayHello(String name, int age, [String? country = 'taiwan']) =>
'Hello $name, you are $age years old, and you are from $country';
// 대괄호, nullable 표시, default value 지정
void main() {
print(sayHello('EJ', 2));
// Hello EJ, you are 2 years old, and you are from taiwan
}
14. QQ Operator (QQ 연산자)
left ?? right
left가 null이면 right를 반환, 아니면 left를 반환
//String cap(String? name) => name.toUpperCase();
//String cap(String? name) => name != null ? name.toUpperCase() : 'ANON';
String cap(String? name) => name?.toUpperCase() ?? 'ANON';
void main() {
cap('ej');
cap(null);
String? name;
name ??= 'ej'; // name이 null이라면 우항의 값 할당
}
15. typedef
자료형에 alias 붙일 수 있게 해 주는 역할
typedef ListOfInts = List<int>;
typedef UserMap = Map<String, String>;
//List<int> reverseList(List<int> list) {
ListOfInts reverseList(ListOfInts list) {
var reversed = list.reversed;
return reversed.toList();
}
//String sayHi(Map<String, String> user) {
String sayHi(UserMap user) {
return 'Hi, ${user['name']}!';
}
void main() {
print(reverseList([1, 2, 3])); // [3, 2, 1]
sayHi({"name": "ej"});
}
Classes
16. class
class 생성 시에는 타입 명시 필수 (함수 안에서는 var 사용 가능한 것과 다름)
final로 선언한 변수는 값 변경 불가능
class Player {
// String name = 'ej';
final String name = 'ej';
int xp = 9797;
void sayHi() {
//this.name; // 가능하지만 class method 안에서는 사용하지 않는 것 권고
print('Hi, I\'m $name!');
// var name = 'nc';
// 위와 같이 같은 이름의 변수 선언 후 Player의 name을 가져오려면 this.name이라고 명시
}
}
void main() {
var player = Player(); // player 인스턴스 생성
// var player = new Player(); new 키워드는 옵션
print(player.name); // ej
// player.name = 'nc'; final 선언하는 순간 값 변경 불가
player.sayHi(); // Hi, I'm ej!
}
17. constructor
argument를 받아서 초기화하는 생성자
class Player {
// 값은 나중에 받아 올 것이기 때문에 late
// late final String name;
// late int xp = 9797;
// Player(String name, int xp) {
// this.name = name;
// this.xp = xp;
// }
// 개선된 코드
final String name;
int xp = 9797;
Player(this.name, this.xp);
void sayHi() {
print('Hi, I\'m $name!');
}
}
void main() {
var player1 = Player('nc', 7979);
player1.sayHi(); // Hi, I'm nc!
var player2 = Player('jo', 7878);
player2.sayHi(); // Hi, I'm jo!
}
18. named constructor parameters
positional argument가 가져올 수 있는 혼란을 방지하기 위해 named arguments로도 선언 가능하다.
can't have a value of 'null' 방지하는 방법 2가지: 1. 기본값 설정, 2. required
class Player {
final String name;
int xp;
String team;
int age;
//Player(this.name, this.xp, this.team, this.age);
// positional arguments를 named arguments로 변경
Player({
required this.name,
required this.xp,
required this.team,
required this.age,
});
void sayHi() {
print('Hi, I\'m $name!');
}
}
void main() {
// var player1 = Player('nc', 7979, 'team1', 22);
var player1 = Player(
name: 'nc',
xp: 7979,
team: 'team1',
age: 22,
);
//var player2 = Player('jo', 7878, 'team2', 20);
var player2 = Player(
name: 'jo',
xp: 7878,
team: 'team2',
age: 20,
);
}
19. named constructors
: 을 이용하여 일부 값만 받고, 일부 값은 미리 설정한 상태로도 생성자 사용 가능
class Player {
final String name;
int xp, age; // 같은 타입 한 줄에 선언 가능
String team;
Player({
required this.name,
required this.xp,
required this.team,
required this.age,
});
Player.createRedPlayer({
required String name,
required int age,
}) : this.age = age, // parameter로 받은 age를 this.age에 할당
this.name = name,
this.team = 'blue',
this.xp = 0;
Player.createBluePlayer(
String name, int age) // 기본적으로 모든 positional arguments는 required
: this.age = age, // parameter로 받은 age를 this.age에 할당
this.name = name,
this.team = 'red',
this.xp = 0;
void sayHi() {
print('Hi, I\'m $name!');
}
}
void main() {
var player1 = Player.createRedPlayer(
name: 'nc',
age: 22,
);
var player2 = Player.createBluePlayer(
'jo',
20,
);
}
20. 예시: json형태로 받아 오는 경우의 class 생성
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 sayHi() {
print('Hi, I\'m $name!');
}
}
void main() {
var apiData = [
{
"name": "ej",
"team": "green",
"xp": 0,
},
{
"name": "nc",
"team": "pink",
"xp": 0,
},
{
"name": "jo",
"team": "blue",
"xp": 0,
},
];
apiData.forEach((playerJson) {
var player = Player.fromJson(playerJson);
player.sayHi();
});
//Hi, I'm ej!
//Hi, I'm nc!
//Hi, I'm jo!
}
21. cascade notation
xx.name = .. , xx. age = .. 처럼 번거롭게 기술하지 않고 .. 을 이용하여 생성 가능하다
class Player {
String name;
int xp;
String team;
Player({
required this.name,
required this.xp,
required this.team,
});
void sayHi() {
print('Hi, I\'m $name!');
}
}
void main() {
// var ej = Player(name: 'ej', xp: 9797, team: 'green');
// ej.name = 'jj';
// ej.xp = 9999;
// ej.team = 'blue';
var ej = Player(name: 'ej', xp: 9797, team: 'green')
..name = 'jj'
..xp = 9999
..team = 'blue';
// 이렇게 불러와서도 가능
var newjj = ej
..name = 'newjj'
..xp = 10000
..team = 'red';
}
22. enums
+) 출력하는 경우 Team.red 등으로 String 형태가 아니라 enum.xx 형태로 출력됨
+) 값만 출력하고 싶은 경우 String 가공할 것
enum Team { red, green, blue } // "" 사용하지 않아도 됨
enum XP { beginner, intermediate, advanced }
class Player {
String name;
XP xp;
Team team;
Player({
required this.name,
required this.xp,
required this.team,
});
void sayHi() {
print('Hi, I\'m $name!');
}
}
void main() {
var ej = Player(name: 'ej', xp: XP.intermediate, team: Team.blue)
..name = 'jj'
..xp = XP.advanced
..team = Team.green;
var newjj = ej
..name = 'newjj'
..xp = XP.beginner
..team = Team.red;
}
23. abstract classes
다른 클래스들이 직접 구현해야 하는 메소드를 모아 놓은 blueprint라고 생각하면 됨
extends로 사용한다
abstract class Human {
void walk();
}
// Human을 extend 한 경우 walk 메소드를 구현해야 함
class Player extends Human {
void walk() {
print('player is walking');
}
}
class Coach extends Human {
void walk() {
print('coach is walking');
}
}
void main() {}
24. Inheritance 상속
class Human {
final String name;
//Human(this.name); // positional arguments 사용
Human({required this.name}); // named arguments 사용
void sayHi() {
print('Hi, I\'m $name!');
}
}
enum Team { red, green, blue }
class Player extends Human {
final Team team;
Player({
required this.team,
required String name,
//}) : super(name); // name을 super 생성자에 전달 (OOP)
}) : super(name: name); // super: 확장한 부모 클래스
@override
void sayHi() {
super.sayHi();
print('I\'m on the ${team} team!');
//Hi, I'm ej!
//I'm on the Team.green team!
}
}
void main() {
var player = Player(team: Team.green, name: 'ej');
player.sayHi();
}
25. mixin
mixin: 생성자(constructor)가 없는 class
class에 property 추가할 때 등 사용함
with로 사용한다
mixin Strong {
final double level = 100;
}
mixin Runner {
void run() {
print('Run!');
}
}
mixin Tall {
final double height = 186;
}
enum Team { red, green, blue }
class Player with Strong, Runner, Tall {
final Team team;
Player({required this.team});
}
class Horse with Strong, Runner {}
class Kid with Runner {}
void main() {
var player = Player(team: Team.green);
}
Dart 온라인 실습 환경: https://dartpad.dev/
https://nomadcoders.co/dart-for-beginners 를 참고하였습니다 🤓
'TIL' 카테고리의 다른 글
실용주의 프로그래머 6장: 동시성 Concurrency (0) | 2024.08.20 |
---|---|
vscode에서 Flutter Android emulator 실행하기 (0) | 2024.08.20 |
실용주의 프로그래머 5장: 구부러지거나 부러지거나 (0) | 2024.07.20 |
실용주의 프로그래머 4장: 실용주의 편집증 (0) | 2024.07.04 |
실용주의 프로그래머 3장 (0) | 2024.06.23 |