👋 빠르게 dart 훑고가기!! (TIL 240120)
수많은 프로그래밍 언어와 마찬가지로 가장 대표적인 자료형은 아래와 같이 존재한다.
String name = "jiyeon";
int age = 22;
bool alive = true;
double credit = 3.333;
이 때 흥미로운 점은
dart에는 num이라는 자료형이 존재하는데, 이는 int와 double값을 모두 가질 수 있는 자료형이다. 또한 , num은 int와 double의 부모 class이다.
List는 Flutter에서 자주 쓰이는 자료형으로 [ ] 대괄호를 이용해 선언할 수 있다.
변수 선언과 마찬가지로 var를 이용해도 되고, List<자료형>처럼 type을 명시해줘도 된다.
var ages = [10, 17, 20, 5, 34];
List<int> ages = [10, 17, 20, 5, 34];
dart는 List내에서 if, for 구문을 이용하여 리스트의 요소를 결정할 수 있는 유용한 문법인 collection if, collection for를 지원한다.
bool alive = true;
var ages = [10, 17, 20, if(alive) 5, 34];
// [10, 17, 20, 5, 34]
var favorite = ["math", "english"]
var subjects = [
"korean",
"science",
for (var fav in favorite) "♥ $fav",
]
text에 변수를 추가하기 위해서는 변수명 앞에 "$"를 붙여 사용하면 된다. 만약 연산이 수행되어야 하는 경우 $ 뒤에 { } 중괄호를 붙여 그 안에 연산식을 작성하면 된다.
var name = "jiyeon";
var greeting = "Hello $name";
var age = 22;
var introducing = "I\'m ${age - 2} years old.";
js/ts의 object, python의 dictionary와 유사한 자료형으로 key와 value로 이루어져있다.
{ } 대괄호를 이용하여 선언하며, key와 value형의 자료형 제한은 없다.
var player = {
"name": "jiyeon",
"age": 22,
"xp": 33.33333,
}
Map<int, bool> answer = {
1: true,
2: true,
3: false,
}
key와 value를 가지는 구조로 object를 만들때 -> class
List와 유사하나 Set의 원소는 중복될 수 없다는 특징을 가진다. { } 중괄호를 이용해 선언한다.
python의 tuple과 기능적으로 유사하다.
var ages = {10, 32, 22, 20 };
ages.add(10);
ages.add(10);
print(ages); // {10, 32, 22, 20}
여러 개의 parameter를 가지는 함수를 작성하는 기본적인 방법은 아래와 같다.
String greeting(string name, int age, string country) {
return "Hello $name, You're age is $age and you come from $country."
}
void main() {
greeting(jiyeon, 22, Korea);
}
하지만 함수의 parameter를 넘겨줄 때 매번 순서를 기억하고 작성하는 것은 매우 번거롭다.
따라서 dart에서는 parameter에 이름을 부여해 순서를 고려하지 않고 이름에 맞는 변수값을 전달할 수 있도록 한다.
named parameter를 사용하기 위해서는 함수의 parameter를 { } 중괄호를 이용해 묶고, 함수를 호출할 때에는 "parameter의 이름 : 값" 형태로 전달해준다.
String greeting({string name, int age, string country}) {
return "Hello $name, You're age is $age and you come from $country."
}
void main() {
print(greeting(
country: "Korea",
name: "jiyeon",
age: 22,
));
}
위의 코드를 실행시키려고 하면 오류가 발생하는데 그 이유는 함수의 인자로 넘겨지는 값이 null이 될 수도 있기 때문이다. null값이 전달되는 것을 방지하기 위해 조치를 취해주어야 한다.
함수 호출시 null값이 전달되는 parameter가 있다면 설정된 default 값이 저장된다.
String greeting({string name="anon", int age=99, string country="none"}) {
return "Hello $name, You're age is $age and you come from $country."
}
void main() {
print(greeting(
country: "Korea",
name: "jiyeon",
age: 22,
));
}
함수의 parameter의 자료형 앞에 required를 붙여 반드시 값을 가져야함을 명시해준다.
required를 사용하게 되면 함수 호출시 값이 없는 인자가 있다면 컴파일 되지 않는다.
String greeting({required String name, required int age,required String country}) {
return "Hello $name, You're age is $age and you come from $country."
}
void main() {
print(greeting()); //error!
}
필수로 전달되지 않아도 되는 parameter를 설정할 때는 [ ] 대괄호 안에 자료형? 변수명 = default 값 으로 작성한다.
String greeting({String name, int age, [String? country = "none"]}) {
return "Hello $name, You're age is $age and you come from $country."
}
?? 연산자를 기준으로 왼쪽에 있는 값이 null이면 오른쪽 값을 return 한다.
String capitalizeName(String? name) => name?.toUpperCase() ?? "ANON";
??= 연산자를 기준으로 왼쪽에 있는 변수의 값이 null이면 오른쪽 값을 할당해준다.
String? name;
name ??= "anon";
복잡한 자료형의 alias를 만들어줄 수 있다. (단, 구조화된 data의 형태를 사용하고 싶다면 class가 더 적절)
typedef UserInfo = Map<String, String>
class의 property를 정의할 때는 자료형을 명시해줘야한다. 변수의 modifier를 이용하여 값의 변경을 막을 수 있다. → final String name = "jiyeon"
또한 class 내부에서는 this를 쓰지 않는 것이 권고된다. (변수와 class property의 이름이 같지 않은 이상!)
class Player {
String name = "jiyeon";
int hp = 100;
void sayHello() {
print("Hi my name is $name");
}
}
void main() {
//인스턴스 생성
var player1 = Player();
//인스턴스에 접근하여 property값 수정
player1.name = "yeonji";
}
생성자 함수의 이름은 class명과 동일해야한다.
class property의 값을 생성자에서 처음 할당해줄 것이라면 변수 선언시 late를 붙여야 한다.
class Player {
late final String name;
late int hp;
Player(String name, int hp) {
this.name = name;
this.hp = hp;
}
}
void main() {
//인스턴스 생성
var player1 = Player("namoo", 524);
}
생성자 함수의 인자 부분에서 바로 property값을 저장하도록 만들 수도 있다.
class Player {
final String name;
int hp;
Player(this.name, this.hp);
}
위의 함수 부분과 마찬가지로 parameter의 개수가 많아지는 경우, named parameter를 이용해 코드를 보다 깔끔하고 명료하게 작성할 수 있다. 방법은 위와 동일하다! (null safety 주의)
class Player {
final String name;
int hp, level;
String sex;
Player({
required this.name,
required this.hp,
required this.sex,
required this.level,
});
}
void main() {
//인스턴스 생성
var player1 = Player(name: "jiyeon", sex: "F", level: 100, hp: 378);
}
기본적인 생성자 메소드와는 다르게 따로 생성자 함수를 작성해줄 수도 있다.
class명.생성자함수이름() : 로 사용하며 콜론 ":"을 사용하는 특징이 있다. 이를 통해 class 객체를 초기화해주는 생성자 함수를 만들 수 있다.
Player.createF({
required String name,
required int age,
}) : this.sex = "F",
this.hp = 0;
Player.createM({
required String name,
required int age,
}) : this.sex = "M",
this.hp = 0;
var playerF = Player.createF(name: "jiyeon", age: 22);
var playerM = player.createM(name: "namoo", age: 26);
class의 object를 생성하고 해당 object의 property나 method를 연속적으로 이용하는 경우, object의 이름을 생략할 수 있게 해주는 문법이다. 이를 사용하기 위해서는 object를 생성할 때 세미콜론을 생략해줘야 하며 ..을 사용하는 가장 마지막 line에만 세미콜론을 붙인다.
void main() {
var jiyeon = Player(name: "jiyeon", sex: "F", level: 100, hp: 378)
..sex = "M"
..level += 1
..sayHello();
}
void main() {
var jiyeon = Player(name: "jiyeon", sex: "F", level: 100, hp: 378);
jiyeon.sex = "M";
jiyeon.level += 1;
jiyeon.sayHello();
}
위의 두 코드는 동일한 작업을 수행한다.
enum은 상수 열거형이라고 하며, 특정 상황에서 쓰이는 것들을 묶어놓은 개념이라고 할 수 있다. C의 define과 유사하다. enum 내부에 쓰인 상수는 자동으로 0,1,2... 와 같은 상수가 매핑된다.
enum변수명.요소로 접근하여 사용할 수 있다.
enum Sex = {F, M}
class Player {
final String name;
int hp,level;
Sex sex;
Player({
required this.name,
required this.hp,
required this.sex,
required this.level,
});
}
void main() {
var jiyeon = Player(name: "jiyeon", sex: Sex.F, level: 100, hp: 378);
}
추상 클래스는 인스턴스화 할 수 없는 class로, 다른 class에게 공통된 특징과 동작을 정의하는데 사용된다.
abstract class Human {
//abstract method 추상메소드
void walk();
}
추상 클래스는 추상 메소드를 가지며 이 메소드는 반환타입, 이름, 매개변수만을 정의한다. 메소드의 실제 구현은 해당 추상 클래스를 상속받는 하위 class에서 해야한다.
class Coach extends Human {
void walk() {
print("I'm walking");
}
}
추상 클래스는 다른 클래스들 사이에 공통된 동작을 추상화하는 데 유용하다. 또한 추상 메소드를 통해 하위 클래스에서 특정 동작의 구현을 강제할 수 있으므로, 다형성과 코드 재사용성을 높일 수 있다.
super라는 키워드를 통해 부모 클래스와 상호작용할 수 있게 해준다. 아래 코드는 Player 클래스가 Human 클래스를 상속받는 예시이다. Player에서 생성자가 호출될 때 부모 class인 Human의 생성자도 호출되어야 하는데 이를 연결해주는 것이 super이다.
class Human {
final String name;
Human(this.name);
void sayHello() {
print("Hello My name is $name");
}
}
enum Team{red, blue, yellow}
class Player extends Human {
final Team team;
Player({
required this.team,
required String name,
}) : super(name);
}
void main(){
var player1 = Player(team: Team.red, name: "jiyeon");
player1.sayHello();
}
Mixin은 생성자가 없는 class를 말한다. 다른 class의 property와 method를 끌어오는 역할을 하며 with 키워드를 사용해 작성할 수 있다.
mixin class Strong {
final double power = 9999.999;
}
enum Team{red, blue, yellow}
class Player with Strong {
final Team team;
final String name;
Player({
required this.team,
required this.name,
});
}
void main(){
var player1 = Player(team: Team.red, name: "jiyeon");
}