- [Flutter] "모두가 할 수 있는 플러터 UI 입문"과 함께한 Dart 기초 문법 요약정리2022년 01월 31일 15시 06분 23초에 업로드 된 글입니다.작성자: DandyNow728x90반응형
"최주호, 정호준, & 정동진. (2021). 모두가 할 수 있는 플러터 UI 입문. 앤써북"으로 플러터 공부를 하고 있다. 최근 Dart 기초 문법을 끝냈으며, 그간 공부했던 언어들(파이썬, 자바스크립트)과 특이점이 있는 부분을 복습하고 기억하기 위해 정리하였다.
// var와 dynamic으로 타입 추론이 가능하나 차이점이 있다. void main() { // int n1 = 1; // double d1 = 10.1; // bool b1 = true; // String s1 = "홍길동"; var n1 = 1; dynamic d1 = 10.1; var b1 = true; var s1 = "홍길동"; // runtimeType를 이용해 타입을 확인할 수 있다. // var로 한 번 초기화된 데이터 타입은 다른 타입으로 변경 불가능. print("var 정수 : ${n1.runtimeType}"); // var 정수 : int print("dynamic 실수 : ${d1.runtimeType}"); // dynamic 실수 : double // dynamic은 var와 달리 다른 타입으로 변경 가능. d1 = 10; print("dynamic 정수 : ${d1.runtimeType}"); // dynamic 실수 : int print("var 부울 : ${b1.runtimeType}"); // var 부울 : bool print("var 문자열 : ${s1.runtimeType}"); // var 문자열 : String }
var와 dynamic으로 타입 추론이 가능하다. 차이점은 var로 한 번 초기화된 데이터 타입은 다른 타입으로 변경할 수 없다. 반면 dynamic은 다른 타입으로 변경이 가능하다.
// 산술 연산자 void main() { print("3 ~/ 2 = ${3 ~/ 2}"); // 몫, 파이썬의 //와 다르게 생겼다. }
산술 연산자에서 몫을 구할 때 파이썬의 경우 // 를 사용한다. Dart에서는 ~/ 이다.
// 조건문 void main() { // 삼항 연산자 print(point >= 60 ? "합격" : "불합격"); // 자바스크립트에서의 사용법과 같아 보인다. // null 대체 연산자 dynamic username = null; print(username); // null print(username ?? "홍길동"); // 홍길동(?? 앞의 값이 null이면 ?? 뒤의 값을 출력하고, 아니면 ?? 앞의 값을 출력) }
if 조건문의 경우 특이점이 없다. 삼항 연산자는 자바스크립트의 경우와 같다. 조건이 맞으면 : 의 왼쪽 값을 출력하고 조건이 맞지 않으면 : 의 오른쪽 값을 출력한다. null 대체 연산자는 생소했다. ?? 앞의 값이 null 인 경우 ?? 뒤의 값을 출력하고, 아니면 ?? 앞의 값을 출력한다.
// 익명함수 // 함수를 매개변수로 전달받을 때는 Function 키워드 사용 void magicBox(Function f) { f(); } // 익명 함수를 인수로 전달할 수 있다. void main() { magicBox(() { print("더하기"); }); }
함수를 매개변수로 전달받을 때는 Function 키워드를 사용한다. 익명 함수를 인수로 전달할 수 있다.
// 변수에 익명 함수를 대입할 수 있다. 이때 Function 타입 사용 Function add = (int n1, int n2) { print(n1 + n2); }; void main() { add(1, 3); }
변수에 익명 함수를 대입할 수 있다. 이때 Function 타입을 사용한다. 자바스크립트의 함수 표현식과 유사해 보인다.
void main() { // 람다식 Function addOne = (n) => n + 1; print(addOne(2)); // 3 // 익명함수 Function addTwo = (n) { return n + 1; }; print(addTwo(2)); // 3 }
위 코드의 결과 값은 동일하다. 각각 람다식과 익명 함수로 작성되었다.
class Dog { String name = "Toto"; int age = 13; String color = "white"; int thirsty = 100; // 객체는 다른 객체와 협력할 수 있다. void drinkWater(Water w) { w.drink(); thirsty = thirsty - 50; } } class Water { double liter = 2.0; void drink() { liter = liter - 0.5; } } void main() { Dog d1 = Dog(); Water w1 = Water(); d1.drinkWater(w1); print("남은 물의 양은 ${w1.liter}"); d1.drinkWater(w1); print("갈증 지수는 ${d1.thirsty}"); }
객체는 다른 객체와 협력할 수 있다.
class Dog { String name = "Toto"; int age = 13; String color = "white"; int thirsty = 100; Dog(this.name, this.age, this.color, this.thirsty) {} // 생성자 } void main() { Dog d1 = Dog("Toto", 13, "white", 100); Dog d2 = Dog("Mango", 2, "black", 50); print("d1의 이름은 ${d1.name}"); print("d2의 이름은 ${d2.name}"); }
생성자는 클래스를 객체로 만들 때 초기화를 위한 함수이다.
// 선택적 매개변수 class Person { String name; int money; Person({this.name = '', this.money = 0}); // 기본 값을 정의할 수 있다. 매개변수를 {}로 감싸야 한다. } void main() { Person p1 = Person(name: "홍길동"); Person p2 = Person(name: "임꺽정", money: 10000); // 인수의 순서나 누락 상관없다. print("${p1.name}의 재산은 ${p1.money}"); // 홍길동의 재산은 0 print("${p2.name}의 재산은 ${p2.money}"); // 임꺽정의 재산은 10000 }
선택적 매개변수를 이용해 기본 값을 정의할 수 있다. 생성자의 매개변수를 {}로 감싸야한다.
class Chef { String name; Chef(this.name); void cook() { print("요리를 시작합니다."); } } void main() { // Chef c1 = Chef("홍길동"); // print("요리사 이름 ${c1.name}"); //요리사 이름 홍길동 Chef c1 = Chef("홍길동")..cook(); // ..는 cascade 연산자이다. 코드 한 줄로 객체를 변수로 넘겨주면서 객체가 가진 함수를 호출할 수 있는 표기법이다. print("요리사 이름 ${c1.name}"); // 요리를 시작합니다. // 요리사 이름 홍길동 }
..는 cascade 연산자이다. 코드 한 줄로 객체를 변수로 넘겨주면서 객체가 가진 함수를 호출할 수 있는 표기법이다.
class Burger { Burger() { print("버거"); } } class CheeseBurger extends Burger { // 부모 생성자 Burger을 상속 CheeseBurger() { print("치즈버거"); } } void main() { CheeseBurger cb = CheeseBurger(); //버거이기도 하고 치츠버거이기도 하다. 다형성 성립 }
부모 생성자 Burger를 상속하여 객체 cb는 버거이기도 하고 치츠버거이기도 하다. 다형성 성립.
class Burger { String name = ''; Burger() {} } class CheeseBurger extends Burger { CheeseBurger(String name) { // this.name = name; super.name = name; // 보모의 객체 참조 } } void main() { CheeseBurger cb = CheeseBurger("치즈햄버거"); print(cb.name); }
super 키워드는 자식이 부모의 객체를 참조할 수 있는 키워드이다.
class Burger { final String name; // final은 상수, 단 한번만 초기화할 수 있고, 반드시 초기화 되어야 한다. Burger(this.name) {} } class CheeseBurger extends Burger { CheeseBurger(String name) : super(name) {} // 이니셜라이져(:) 키워드는 생성자의 내부 스택이 실행되기 전에 다른 무언가를 호출하고 싶을 때 사용한다. 이 코드에서 이니셜라이즈를 사용하지 않으면 final 변수가 초기화 되지 않아 오류가 발생한다. } void main() { CheeseBurger cb = CheeseBurger("치즈햄버거"); print(cb.name); }
final은 상수이다. 단 한 번만 초기화할 수 있고, 반드시 초기화되어야 한다. 이니셜 라이져(:) 키워드는 생성자의 내부 스택이 실행되기 전에 다른 무언가를 호출하고 싶을 때 사용한다. 위 코드에서는 이니셜 라이즈를 사용하지 않으면 final 변수가 초기화되지 않아 오류가 발생한다.
class Engine { int power = 5000; } class Wheel { String wheelName = "4륜 구동 바퀴"; } class BMW with Engine, Wheel { } // Mixin은 여러 클래스 계층에서 클래스의 코드를 재사용하는 방법. 다중 상속 문제 해결, 컴퍼지션 사용 없이 다른 클래스 코드 재사용 가능. void main() { BMW b = new BMW(); print(b.power); print(b.wheelName); BMW c = BMW(); // new를 붙이지 않아도 객체가 만들어 진다. print(c.power); print(c.wheelName); }
Mixin은 여러 클래스 계층에서 클래스의 코드를 재사용하는 방법이다. 다중 상속 문제 해결하고, 컴퍼지션 사용 없이 다른 클래스 코드를 재사용할 수 있다.
// 추상 클래스 abstract class Animal { void sound(); // 구체화되지 않은 추상 메서드 } class Dog implements Animal { void sound() { print("멍멍 배고파"); } } class Cat implements Animal { void sound() { print("야옹 배고파"); } } class Fish implements Animal { void sound() { print("뻐끔뻐끔 배고파"); } } void start(Animal a) { a.sound(); } void main() { start(Dog()); start(Cat()); start(Fish()); }
추상 클래스는 구체화되지 않은 추상 메서드를 가지고 있다. 자식 클래스는 구체화된 메서드를 오버라이드 한다.
// 컬렉션 import 'dart:math'; // Random 클래스용 라이브러리 void main() { List<int> nums = [1, 2, 3, 4]; // 리스트 print(nums[0]); Map<String, dynamic> user = {"id": 1, "username": "cos"}; // 맵 print(user["id"]); // Set을 이용한 로또 번호 생성하기 Set<int> lotto = {}; // Set, 집합 표현, 중복 허용 안함, 순서 없음. Random r = Random(); lotto.add(r.nextInt(45) + 1); lotto.add(r.nextInt(45) + 1); lotto.add(r.nextInt(45) + 1); lotto.add(r.nextInt(45) + 1); lotto.add(r.nextInt(45) + 1); lotto.add(r.nextInt(45) + 1); print(lotto); List<int> lottoList = lotto.toList(); // List 타입으로 변경 lottoList.sort(); print(lottoList); }
리스트는 파이썬과 동일하다. 맵은 파이썬의 딕셔너리를 닮았다. Set은 집합을 표현하며 중복을 허용하지 않고, 순서가 없다. Random 클래스의 사용법도 잘 봐두자!
// map 함수 void main() { var chobab = ["새우초밥", "광어초밥", "연어초밥"]; var chobabChange = chobab.map((e) => "간장_" + e); print(chobabChange); // (간장_새우초밥, 간장_광어초밥, 간장_연어초밥) }
컬렉션의 Map와 혼동하지 말자! 파이썬의 map과 유사한 기능이다. iterable object에 사용한다.
// 필터링. 컬렉션에 담긴 데이터를 삭제할 때 많이 사용 void main() { var chobab = ["새우초밥", "광어초밥", "연어초밥"]; var chobabChange = chobab.where((element) => element != "광어초밥"); print(chobabChange); // (새우초밥, 연어초밥) }
where는 필터링 기능이다. 컬렉션에 담긴 데이터를 삭제할 때 많이 사용한다.
void main() { // 복사1 var list = [1, 2, 3]; var list1 = list; // 참조에 의한 호출 var newList = [...list]; // 복사, 값에 의한 호출 newList[0] = 500; print(list); // [1, 2, 3] print(list1); // [1, 2, 3] print(newList); // [500, 2, 3] // 복사2 var list2 = [ {"id": 1}, {"id": 2} ]; var newList2 = list2.map((i) => {...i}).toList(); // 리스트 내에 있는 맵을 복사할 경우 {} 안에서 스프레드 연산자 사용. print(newList2); // [{id: 1}, {id: 2}] print(list2.hashCode); // 400195542 print(newList2.hashCode); // 389943684, 주소가 list2와 다르다. 복사된 값에 의한 호출이라는 의미. newList2[0]["id"] = 500; print(list2); // [{id: 1}, {id: 2}] print(newList2); // [{id: 500}, {id: 2}] print(list2.hashCode); // 533114396 print(newList2.hashCode); // 1051251238 // 추가 var list3 = [1, 2, 3]; var newList3 = [...list, 4]; // 컬렉션 데이터 추가 print(list3); // [1, 2, 3] print(newList3); // [1, 2, 3, 4] // 수정 var users = [ {"id": 1, "username": "cos", "password": 1234}, {"id": 2, "username": "ssar", "password": 5678}, ]; var newUsers = users.map((user) => // user["id"] == 2 ? {"id": 2, "username": "love", "password": 5678} : user); user["id"] == 2 ? {...user, "username": "love"} : user); // 위와 동일한 코드 print(users); // [{id: 1, username: cos, password: 1234}, {id: 2, username: ssar, password: 5678}] print(newUsers); // ({id: 1, username: cos, password: 1234}, {id: 2, username: love, password: 5678}) }
스프레드 연산자 ... 는 값을 복사, 추가, 수정한다. 리스트 내에 있는 맵을 복사할 경우 {} 안에서 스프레드 연산자를 사용한다. 삼항 연산에 사용된 스프레드 연산자도 눈여겨보자!
// final, const는 둘다 상수. final은 runtime때 초기화, const는 compile때 초기화. class Animal { final String name; const Animal(this.name); } void main() { Animal a1 = const Animal("사자"); Animal a2 = const Animal("사자"); Animal a3 = const Animal("기린"); print(a1.hashCode); // 970471807 print(a2.hashCode); // 970471807 // 생성자 인수가 동일하기 때문에 객체를 재사용 print(a3.hashCode); // 197636435 // 생성자 인수가 다르기 때문에 새로운 객체 생성 }
final, const는 둘 다 상수를 선언하는 키워드이다. final은 runtime때 초기화, const는 compile때 초기화된다. 생성자 인수가 동일할 때는 객체를 재사용하고, 다를 때는 새로운 객체를 생성한다. compile때 초기화되면 런타임 때 속도가 빠르다. 동일한 클래스를 객체로 여러 번 만들어야 하는 경우에 생성자 인수의 값이 동일하면 같은 객체이기 때문에 메모리에 만들어진 객체를 재사용한다. const를 잘 활용하면 flutter에서 그림을 효율적으로 그릴 수 있다.
class Person { // String name; // int age; // Person({required this.name, required this.age}) // required 붙이면 선택적 파라미터이지만 값을 무조건 받아야 한다. String? name; // 자료형 뒤에 ?를 붙이면 null을 받을 수 있는 타입이 된다. int? age; Person({this.name, this.age}); } void main() { Person p = Person(age: 30); String name2 = p.name ?? "이름없음"; // null 대체연산자 값 받기, 디폴트값 "이름없음" int age2 = p.age ?? 0; print(p.name); // null print(p.age); // 30 print(name2); // 이름없음 print(age2); // 30 }
Dart 2.12 버전부터 Null Safety가 적용된다. 자료형 뒤에 ? 를 붙이면 Null Safety가 적용되어 null을 받을 수 있는 타입이 된다. required를 생성자 파라미터의 변수명 앞에 붙이면 선택적 파라미터이지만 값을 무조건 받아야 한다.
728x90반응형'언어·프레임워크 > Flutter' 카테고리의 다른 글
[Flutter] "모두가 할 수 있는 플러터 UI 입문" - 레시피 앱 만들기 (0) 2022.02.01 [Flutter] "모두가 할 수 있는 플러터 UI 입문" - 스토어 앱 만들기 (0) 2022.02.01 [Flutter] fluttertoast 라이브러리 사용시 만난 에러(error) #2 (0) 2022.01.22 [Flutter] Flutter Layout 참고 자료 (0) 2022.01.21 [Flutter] fluttertoast 라이브러리 사용시 만난 에러(error) (0) 2022.01.20 다음글이 없습니다.이전글이 없습니다.댓글