Search
Duplicate

CH.2 다트

목차

Chapter Point

다트의 Hello, World
제어 흐름, 루프, 함수 등 기본 문법
I/O 라이브러리 사용법
다트 프로그램 해부
객체지향 프로그래밍

1. Hello, Dart

개발자로써 새로운 언어를 시작하면 꼭 거쳐가야할 단계가 있다.
바로 Hello World! 출력해보기! 학교를 다닐적에 입학 후 첫 수업에서 Hello World 출력은 여러모로 기억에 남는 추억이다. 그 이후로 자바, C+, javascript, python등 새로운 언어를 시작할 때마다 한 번씩은 출력을 했던 것 같다. 여하튼 Dart에서 Hello Dart를 출력해보자.
void main() { print("Hello, Dart!"); }
Dart
복사
이걸 어디서 실행시킬지는 각자의 자유다. 터미널에서 직접 코드를 쳐도 되고, 각각이 사용하는 IDE를 사용해도 된다. 나같은 경우에는 Visual Studio Code를 사용해 코드를 실행시켰다.
혹시몰라 해당 과정도 첨부한다.
VSCode로 Hello Dart 출력하기

1.1 다트 프로그램 해부

다트의 main 함수
main은 다트 프로그램에서 항상 처음으로 실행되는 코드이다.
모든 프로그램에는 main함수를 포함해야 한다.
반환타입은 void로 반환타입이 없는 경우를 뜻한다.
코드는 세미콜론(;)으로 끝난다. 다트의 모든 행은 세미콜론으로 마치는 것이 규칙

1.2 함수 호출및 간단한 리팩토링

우리는 Hello, Dart를 출력해봤고, 함수에서 사용되는 키워드들의 대략적인 의미를 알았다.
그렇다면, 이번에는 다음 문자들을 콘솔로 출력하는 프로그램을 만들고 간단한 리팩토링까지 해보자.
Hello, World! Hello, Catsbi! Hello, Earth! Hello, Pipo! Hello, Crong!
Plain Text
복사
1.
무작정 출력하기
void main() { print("Hello, World!"); print("Hello, Catsbi!"); print("Hello, Earth!"); print("Hello, Pipo!"); print("Hello, Crong!"); }
Dart
복사
5개의 문자를 무작정 print로 출력해 보았다. 하지만, 5개는 이렇게 했다지만 출력해야할 문자가 100개 1000개쯤 되면, 너무 비효율적이다. 그렇기에 우선 출력을 담당할 함수를 만든다.
2.
출력 함수 만들기
void main() { helloDart(); //함수 호출 } void helloDart() { print('Hello, Dart'); }
Dart
복사
이제 main에서 helloDart 함수를 호출하면 Hello, Dart가 출력된다.
하지만, 이 함수는 Hello Dart만 출력한다. 다른 문자들을 출력할 수 있도록 하려면 호출할 때 받는 매개변수를 출력하도록 해야한다.
3.
매개변수를 받는 함수 구현
void main() { helloDart("World!"); helloDart("Catsbi!"); helloDart("Earth!"); helloDart("Pipo!"); helloDart("Crong!"); } void helloDart(String name) { print('Hello, $name'); }
Dart
복사
매개변수 name 을 받으면 템플릿 리터럴을 통한 문자열 결합으로 해당 문자열을 출력해주는 함수를 만들었다. 하지만, main에서 직접 출력이 아닌 함수 호출을 한다는것을 제외하면 아직도 문자열마다 함수 호출하는 것은 동일하다 이를 배열 혹은 컬렉션 자료구조를 이용해 해결한다.
4.
List자료구조및 반복문 이용
void main() { List<String> greetings = ["World!", "Catsbi!", "Earth!", "Pipo!", "Crong!"]; for (var name in greetings) { helloDart(name); } } void helloDart(String name) { print('Hello, $name'); }
Dart
복사
이렇게 각각의 이름을 배열로 모은 뒤 반복문을 통해 각각의 이름을 함수에 전달해 출력하도록 리팩토링을 완료해봤다.

1.3 입출력과 다트 라이브러리

개발환경 구축시 설치한 Dart-SDK 는 다양한 라이브러리를 제공하는데, 이 중 dart:core 라이브러리만 자동으로 로드된다. 그래서 그밖에 dart:html, dart:async, dart:mach 등 다양한 라이브러리를 사용하기 위해서는 직접 로드를 해줘야 한다. 그 방법은 아래와 같다.
import 'dart:io';
Dart
복사
입출력을 지원하는 I/O 라이브러리를 로드하는 코드다.

1.2 다트의 프로그래밍 개념

다트는 객체지향 언어이며 단일 상속(single inheritance)을 지원한다.
다트에서 모든 것이 객체이며 모든 객체는 클래스의 인스턴스다. 모든 객체는 Object 클래스를 상속받는다. (숫자도 기본형이 아닌 객체다.)
다트는 형식을 갖는다(typed) 문자열을 반환하다고 선언한 함수에서 숫자를 반환할 수 없다.
다트는 최상위 수준 함수와 변수를 지원하며 이를 라이브러리 멤버라 부른다.
다트는 어휘적(exically)으로 한정된다.

1.2.1 다트의 형식 시스템

언어마다 형식 시스템의 엄격함은 다르다. Java나 C와같이 엄격한 형식을 가진 언어도 있고, 자바스크립트, 파이썬, 루비와 같은 자유로운 형식 시스템도 있다.
다트를 할 때 알아야 하는 형식 시스템에 대해 알아보자.

변수를 선언할 때 형식을 지정해야 한다.

String name; int age;
Dart
복사
형식을 사용하면 해당 변수에 들어갈 값의 형식을 지정해줄 수 있는데, 이처럼 변수의 형식을 지정해주면 그외의 형식으로는 값을 대입할 수 없다.
int age = 10; // 문제없이 대입 int price = "천원"; // int 타입의 값 변수의 String을 대입하니 에러
Dart
복사
이 대입 코드를 강제로 실행시키면 아래와 같은 에러가 발생한다. 형식 안정성(type safe)덕에 발생한 오류다.
Error: A value of type 'String' can't be assigned to a variable of type 'int'. int price = "천원";
컴파일 시점에서 형식을 검사하기에 런타임 버그를 줄일 수 있다. 기본형 변수외의 형식도 알아보자.
복합 데이터 형식
ListMap을 사용하는 경우에는 <형식>을 통해 정의한다.
List<String> names; //문자열 리스트 List<int> ages; //정수 리스트 Map<String, int> people;//문자열 키와 정수 값을 갖는 맵
Dart
복사
함수의 형식
main 함수의 반환 형식은 void로 반환타입이 없다. 하지만 특정 형식을 반환할수도 있다.
int addNumber10(int number) { return number + 10; }
Dart
복사
매개변수에 10을 더해 반환하는 함수(addNumber10)다. 반환형식은 void가 아닌 int이기에 int형식의 값이 반환된다는 것을 알 수 있다.
동적 형식
다트는 정적 형식 뿐아니라 동적 형식(dynamic type)도 지원한다. 변수를 동적(dynamic)으로 설정하면 컴파일러가 해당 변수에 모든 형식을 허용한다.
dynamic myNumber = 'Hello'; var myString = 'Hello';
Dart
복사
dynamicvar 둘 모두 대입하는 값에 따라 컴파일러가 해당 변수의 형식을 허용해준다.
하지만, 차이점이 존재하는데 dynamic키워드의 변수는 특정 형식의 값을 대입후에 다른 형식의 값을 넣어도 문제가 없지만 var 키워드를 사용하면 최초 대입하는 값의 형식을 사용하면 그 이후에는 다른 형식을 대입할 수 없다. 즉 변수의 형식을 한 번 결정하면 바꿀 수 없다.
그리고, 함수도 반환 형식을 생략할 수 있다.
myPrint() { print('hello'); }
Dart
복사
이렇게 형식을 생략한 함수는 컴파일러가 함수의 형식을 자동으로 추론하는데, 위 myPrint함수는 값을 반환하지 않기 때문에 반환값을 변수에 할당하면 오류가 발생한다.

언제 동적 형식을 사용할까?

사람이라는 객체의 정보를 Map에 담는다고 하면 이름, 나이, 주소, 전화번호, 등등의 여러 정보들이 들어간다. 이런 정보는 숫자 형식도 있고 문자 형식도 있을텐데 이런 경우 동적 형식인 dynamic은 유용하게 사용된다.
Map<String, dynamic> human;
Dart
복사
동적 형식 키워드중에는 var도 있다는 것을 알고 있지만, 이 키워드는 변수를 정의할때만 사용할 수 있다. var를 사용할 수 있는 범위는 한정적이며 되도록 정적 형식을 사용하는 것이 좋다.

1.2.2 주석

다트는 세 가지 주석을 지원한다.
// 인라인 주석 /* 블록 주석. 다트의 블록 주석은 그리 편하지 않다. */ ///문서 /// /// 클래스의 문서화에 사용하는 주석
Dart
복사

1.2.3 변수와 할당

name이라는 변수를 선언했으며 값은 할당되지 않은 상태이기에 null 값을 갖는다 코드 스타일 가이드(1)에 따라 명시적으로 null을 할당하지는 말자.
final, const, static
String name;
Dart
복사
const String name = 'Catsbi'; const String name2 = 'Catsbi $secondName'; //Error
Dart
복사
const 키워드는 변수의 값을 바꿀수 없도록(immutable)하는 키워드로 컴파일 이후에는 변경될 수 없어야 한다. 그렇기에 위 코드에서 두 번째 코드는 컴파일 이후에 템플릿 리터럴($secondName)에 의해 변경될 수 있기 때문에 사용이 불가하다.
class Person { final String name; Person(this.name); } void main() { Person p = new Person("Catsbi"); print(p.name); }
Dart
복사
final키워드는 한 번만 할당할수 있으며 클래스 수준에서 변수를 할당하기 전에 선언한다.
위처럼 클래스에 생성자(constructor)에 할당하는 변수에 final을 사용한다.
class Car { final String name; static const wheelCount = 4; Car(this.name); static void printExhaustsound(){ print("부릉 부릉"); } } void main() { print(Car.wheelCount);// 4 Car.printExhaustsound();// 부릉 부릉 }
Dart
복사
마지막으로 static 키워드는 변수 혹은 메서드의 스코프를 글로벌로 넓혀줍니다. 그래서 클래스에 static을 선언한 변수, 메소드는 인스턴스 별로 생성되는게 아니기 때문에 클래스에 직접 접근해 호출이 가능합니다.

1.2.4 널 인지 연산자(null-aware)

?., ??, ??=
이 객체가 null이면 오류를 발생하지도 말고, 아무것도 하지말라고 지시하는 연산자.
?. 연산자
API를 통해 유저 정보를 받아온다고 할 때 사용자 정보의 유무를 확인 할 수 없는 경우 다음과 같이 코드를 작성한다.
void getUserAge(String username) async { final request = new UserRequest(username); final response = await request.get(); User user = new User.fromResponse(response); if (user != null) { this.userAge = user.age; } // 생략 }
Dart
복사
조건문 if를 통해 user 가 null인지 확인을 한 뒤 유저 정보중 나이(age)를 할당 해주는데, 널-인지 연산자(?.)를 사용하면 더 간결하게 코드 작성이 가능하다.
void getUserAge(String username) async { ... this.userAge = user?.age; ... }
Dart
복사
이와 같이 작성을 하면 user가 null이면 오류를 발생하지 않고 userAge에 null을 할당하고 아닐 경우 정상적으로 유저의 나이를 대입한다.
?? 연산자
해당 값이 없는 경우 에러를 발생시키지 않는 것까지는 좋다. 하지만 여기서 더 나아가 null이 아닌 default value를 넣어주고 싶다면 ?? 연산자를 쓰면 된다.
void getUserAge(String username) async { ... this.userAge = user.age ?? 18; ... }
Dart
복사
유저의 나이정보가 있을경우 해당 정보를 할당하고 아닐 경우 18이라는 기본값을 할당한다.
연산자를 사용한다고 객체 그래프 탐색을 너무 자유롭게 할 수는 없다. 예를 들어, User라는 객체가 Null일 경우 User.age ?? 18 은 Null 객체에서 age라는 필드 값을 탐색해야하기때문에 ?? 연산자에 도달하기 전에 에러가 발생하므로 주의할 필요가 있다.
??= 연산자
객체가 null이면 기본값을 할당하고 아니면 객체를 그대로 반환하는 연산자.
int x = 5 x ??= 3;
Dart
복사

1.3 제어 흐름

Dart는 다른 언어들과 마찬가지로 조건문, 반복문 등 여러 문법을 통해 로직의 흐름을 제어할 수 있다.

1.3.1 if와 else

대부분 언어와 같이 다트 역시 조건문 (if, else if, else)를 지원한다.
Cup cup = new Cup(); cup.set(getDrink()); if(cup.getDrinkType() == "Coffee"){ print("커피입니다"); } else if(cup.getDrinkType() == "Orange Juice") { print("오렌지 주스입니다."); } else { print("알 수 없는 음료입니다."); }
Dart
복사
if(평가식){ 코드 }
: if문은 평가식을 평가 한 뒤 참인지 거짓인지(Boolean 타입) 평가 결과에따라 참일 경우 블록내의 코드를 수행하고 아닐경우 다음으로 넘어갑니다.
else if(평가식) { 코드 }
: else if는 and를 의미하는 논리연산자의 && 혹은 or을 의미하는 || 를 표현하는데 최초 조건문의 평가식이 거짓일 경우 else if의 괄호 내의 평가식을 평가합니다. 그래서 그 평가식이 참일경우 블록내의 코드를 수행하고 아닐경우 다음으로 넘어갑니다.
else { 코드 }
: else는 독립적으로는 사용하지 못하고 if문과 함께써야하는데 위에 위치한 if (혹은 else if) 조건문의 평가식이 모두 거짓일 경우 해당 블록내의 코드가 수행됩니다.
다트는 다른 언어와 달리 평가식의 결과값의 형식이 Boolean 타입만 유효합니다.
if문 을 여러번 쓰는 것과 else if를 사용하는것의 차이점
: 위 코드에서 굳이 오렌지 주스인지 판단하는 것을 else if로 안하고 if로 하면 안되었을까요? 결과만 따지고 보면 위 코드에서 else if를 if로 바꿔도 에러는 발생하지 않습니다.
하지만, 만약 cup.getDrinkType() 이 Coffee였다면 어떨까요? 물론 결과는 동일하게 '커피입니다'가 출력됩니다. 하지만, 그 이후에 if(cup.getDrinkType() == "Orange Juice") 코드는 수행되어 드링크 타입을 평가할 것입니다. 위에서 이미 커피인게 밝혀졌음에도 불구하고 말이죠.
if와 else if로 조합하는 경우 제어 흐름은 이 평가식 중 참인 결과가 있다면 해당 코드가 수행되고 해당 if 와 else if 분기를 건너뛴다. 하지만 if를 여러개 둘 경우 맨 상단에 위치한 if문이 참일 지라도 내부 코드를 수행 후 아래 코드를 수행하게 된다.
1~3번까지의 if, else if, else 중 하나라도 참이면 나머지를 다 건너뛰고 4번이 수행된다.
어떤 분기가 참이던 거짓이던 상관없이 모든 조건문을 평가한다

1.3.2 switch 와 case

내 나이를 기준으로 성인인지 미성년자인지 구분하는건 참/거짓만으로 구분할 수 있어 if문으로 충분하다. 하지만 변수에 값이 2개 이상 여러개라면 switch문을 사용하면 된다.
void main() { Cup cup = new Cup(); cup.setDrinkType(getDrink()); switch (cup.getDrinkType()) { case 'coffee': print('커피입니다.'); break; case 'orange juice': print('오렌지 주스입니다.'); break; default: print("알 수 없는 음료입니다."); } }
Dart
복사
break 아니면 return 문이 있어야 switch case문을 벗어날 수 있다.
런타임 상수만 사용 가능하기에 평가식을 넣을 수는 없다.
switch문에서 case에 break나 return문을 사용하지 않으면 자동으로 다음 case를 실행한다.
int number = 1; switch (number) { case 1: case 3: case 5: print("positive!"); break; default: print("zero!"); break; }
Dart
복사
위 코드를 실행하면 case 1: 에 매치되지만 break가 없기에 case 5:에서 멈춰 positive가 수행된다.
switch 문 탈출
switch 문에서 각 case에서 switch 문을 탈출하는 키워드를 사용해 해당 문법을 벗어나는 제어 흐름을 만드는데, 이러한 탈출 키워드를 추가하지 않으면 에러가 발생한다.
break : 해당 switch 문을 탈출하여 다음 코드를 수행한다.
return : 함수 실행 자체가 종료되며 함수의 반환 타입에 맞는 반환값을 넣어주지 않으면 에러가 발생한다.(void 일경우 반환타입이 없기에 반환값을 입력하지 않고 return만 작성한다.)
throw : 오류를 의도적으로 발생시키는 키워드
continue : 해당 레이블의 case부터 실행시키는 키워드
int number = 1; switch (number) { case 1: print("1"); continue also10; case 3: case 5: print("positive!"); return; also10: case 10: print("10"); break; default: print("zero!"); break; }
Dart
복사
[실행 결과]

1.3.3 삼항 연산자

(평가식) ? 참일경우 : 거짓일경우
평가식의 결과가 참일 경우 물음표 뒤의 콜론을 기준으로 전자의 옵션을 반환하고, 거짓일 경우 후자의 옵션을 반환한다.
조건문을 삼항 연산자를 사용해서 좀 더 간결하게 표현할수도 있다.
//기존의 if문을 이용한 제어 흐름 if (cup.getDrinkType() == "coffee") { print('커피입니다.'); } //삼항 연산자를 이용한 제어 흐름 String result = cup.getDrinkType() == "coffee" ? "커피입니다" : "알 수 없는 음료입니다."; print(result);
Dart
복사

1.3.4 반복문

다른언어와 마찬가지로 반복문역시 지원한다.
표준 for
while
do while
for-in
forEach

표준 for

표준 for
보통 인덱스가 필요한 경우 표준 for loop를 상요하며 for문 괄호 내에 초기값을 선언해 해당 값이 반복 조건 평가식으로 평가를해서 값이 false가 될 때까지 코드를 실행하고 증감식을 수행한 뒤 다시 조건 평가를 합니다.
for (var i = 0; i < 5; i++) { print('index: $i'); }
Dart
복사
실행 결과

for - in

인덱스가 필요 없다면 for - in loop를 사용 할 수 있다. 이터레이션 프로토콜을 따르는 자료구조라면 모두 간단하게 사용할 수 있다.
List<String> fruits = ['apple', 'grape', 'orange', 'mango']; for (var fruit in fruits) { print("과일: $fruit"); }
Dart
복사

forEach

함수형 프로그래밍으로 forEach를 사용할수도 있다. 역시 이터레이션 프로토콜을 지원하는 자료구조라면 모두 forEach를 사용할 수 있다. 그리고 또 다른 특징으로 forEach는 해당 이터레이터에서 호출하는 함수로 호출 시점에서 새로운 스코프를 만들기에 forEach에서 접근한 모든 값은 이 블록 외부에서는 접근할 수 없다.
List<String> fruits = ['apple', 'grape', 'orange', 'mango']; fruits.forEach((fruit) => print('과일: $fruit'));
Dart
복사

while

괄호내의 평가식이 false가 될 때까지 블록내의 코드를 수행합니다. 블록내부를 수행하기전에 조건식을 검사하기 때문에 코드가 아예 실행되지 않고 넘어갈수도 있습니다.
int count = 0; while (count < 5) { print(count); count++; }
Dart
복사

do - while

while문과 동일하지만, while문이 평가식을 평가한 뒤에 결과에따라 블록 내의 코드를 수행한다는 점과는 다르게 do - while은 우선 do 블록내의 코드를 1회 수행 후 평가식을 평가한다는 점이 다릅니다.
int count = 0; do { print(count); count++; } while (count < 5);
Dart
복사

반복문의 제어 흐름 키워드

이러한 반복문들이 수행되다가 특정 조건에서 이 반복문의 흐름을 멈추거나 넘기고 싶을때 즉, 제어흐름을 하고자 할 때 사용하는 키워드(break, continue)가 있습니다.
break : 루프를 완전히 탈출는 키워드
for (var i = 0; i < 5; i++) { if (i == 3) { break; } print(i); }
Dart
복사
continue: 루프의 다음 차례로 한차례 넘기는 키워드
for (var i = 0; i < 5; i++) { if (i == 3) { continue; } print(i); }
Dart
복사

1.4 함수

함수의 기본적인 구조
다트는 객체지향 언어이기에 함수 자체로도 객체이며 Function이라는 형식을 가진다.
그렇기에 함수를 값으로 전달하거나 변수에 할당역시 가능하다. 이를 고차함수(high-order function)라 한다.
다트는 화살표 함수(arrow function)을 지원한다.
String makeGreeting(String name) => 'Hello, $name';
Dart
복사
위 형식에서는 return이 생략되었지만 실제로는 return 'Hello, $name'과 동일하다.

1.4.1 파라미터

다트 함수는 다음과 같은 여러 파라미터를 지원한다.
1.
위치 지정(positional) 파라미터
2.
이름 지정(named) 파라미터
3.
선택형 위치 지정(optional positional)파라미터
4.
선택형 이름 지정 파라미터

위치 지정(positional)파라미터

지금까지 예제를 통해 살펴본 함수들은 모두 위치 지정 파라미터였다.
void printNameAndAge(String name, int age){ print('myName is $name and my Age is $age'); }
Dart
복사
이 함수를 호출할 때는 name과 age를 차례대로 순서에 맞춰서 전달해야 한다.
printNameAndAge(33, 'catsbi')이런식으로 순서를 지키지 못하면 에러가 발생한다.

이름 지정(named) 파라미터

함수를 호출할 때 인수를 레이블과 쌍으로 제공한다.
void printNameAndAge({String name, int age}) { print('myName is $name and my Age is $age'); } ... printNameAndAge(name: 'Catsbi', age: 33);
Dart
복사
중괄호({})로 이름 지정 파라미터를 감싸 이름 지정 파라미터를 구현합니다.
기본적으로, 이름 지정 파라미터는 선택적(Optional)이다. 그래서 이름을 지정하지 않고도 호출할 수 있는데 만약 이러한 선택적인 부분을 필수적(Required)으로 바꾸고 싶다면 @required 애너테이션을
사용하면 된다. (해당 애너테이션은 meta 라는 라이브러리를 사용해야 한다.)

선택형 위치 지정 파라미터

대괄호([])를 이용해 선택형 위치 지정 파라미터를 정의할 수 있습니다.
void printNameAndAge(String name, int age, [String job]) { job = job ?? '백수'; print('myName is $name and my Age is $age and my Job is $job'); }
Dart
복사
선택형 파라미터인 job은 선택형이기에 인수를 전달하지 않아도 오류가 발생하지 않습니다.
함수 시그니처에 = 연산자를 이용해 파라미터의 기본값을 정의할 수 있다.
void printNameAndAge(String name, int age, [String job = "백수"]) { print('myName is $name and my Age is $age and my Job is $job'); }
Dart
복사

1.4.2 고급 함수 개념

우리는 챕터 1.2 부분에서 간단한 리팩토링을 구현해봤었다. 이렇게 기능을 나눠 기능을 구현하는 행위를 추상화(abstraction)라 한다. 우리는 이렇게 추상화를 활용해 기능들을 나눔으로써 추후 유지보수를하거나 코드를 분석할 때 쉽게 이해할 수 있다.
더하여, 우리는 화살표함수를 통해 고차함수에 대해서도 알고있다. 고차함수를 통해 함수를 인수로 받거나 함수로 반환하면서 다른 함수를 이용할 수 있다.
void printNumsPlus10(int num){ print(num + 10); } nums.forEach(printNumsPlus10);
Dart
복사

1.4.3 어휘 스코프

각 코드 블록은 위에서 정의한 모든 변수에 접근이 가능하다.
코드의 구조로 범위를 결정하며 중괄호가 열리는 부분부터 닫히는 부분까지로 범위를 확인한다.
내부 블록에서 외부 블록에 접근은 허용된다.
외부에서 내부 블록에 접근은 불가능하다.

1.5 다트의 객체지향 프로그래밍

우선 객체지향(OOP)에 대한개념은 기본적으로 따로 공부를 했다고 가정하고 이 부분은 포인트만 잡고 넘어가는 식으로 포스팅을 하겠다.

1.5.1 클래스

클래스는 객체의 청사진으로 객체를 묘사한다.
물리적 물체, 개념, 이벤트, 논리적 형용사의 그룹 등을 코드로 표현하고자 할때 클래스를 사용한다.
class TransactionEvent { //프로퍼티와 메서드 }
Dart
복사
다만, 다트에서는 인스턴스를 만들때 new 키워드를 사용하는 것이 선택적(Optional)이다. 컴파일러가 자동으로 알맞은 키워드를 추론하기 때문인데, 무튼 다트에서는 그렇기에 new 키워드 사용을 권장하진 않는다.
class Cat { String name; String color; } Cat pipo = Cat(); // new를 사용하지 않아도 컴파일러가 자동으로 키워드를 추론한다. pipo.name = 'Pipo'; pipo.color = 'Yellow';
Dart
복사

1.5.2 생성자

새 인스턴스를 만들 때 수행할 동작을 생성자(constructor)를 통해 지정할 수 있다.
생성자를 통해 인스턴스를 생성할 때 필요한 초기값 혹은 초기화 로직을 수행할 수 있다.
class Animal { String name; String type; Animal(String name, String type) { this.name = name; this.type = type; //추가적인 초기화 로직 } }
Dart
복사
다트는 여기서 더 간결하게 줄일수도 있다.
// case 1 Animal(this.name, this.type); //case 2 Animal(this.name, this.type){ //추가적인 초기화 로직 }
Dart
복사

1.5.3 상속

다트 역시 자바나 자바스크립트(ES6 +) 처럼 상속을 지원하며 extends 키워드를 사용하여 상속을 할 수 있다.
동물 클래스인 Animal을 기준으로 상속 예제를 구현해 보자.
class Animal { String name; int legCount; } class Amphibian extends Animal { String isPoisonous; } class Mammal extends Animal { String furColor; } class Frog extends Amphibian { void makeNoise() { print('개굴'); } } class Cat extends Mammal { void makeNoise() { print('야옹'); } } class Pig extends Mammal { void makeNoise() { print('꿀꿀'); } }
Dart
복사
여기서 Cat, Pig 클래스는 Mammal 클래스를 상속받았기에 Mammal 클래스의 프로퍼티인 furColor를 이용할 수 있다. 그리고 Mammal 역시 Animal을 상속받았기 때문에 pig는 name과 legCount 프로퍼티도 이용할 수 있다. 아래 그림을 통해 한눈에 위 코드를 파악해보자.
객체지향 상속 예제
각각의 클래스는 자신이 상속하는 부모 클래스의 프로퍼티와 메소드를 사용할 수 있다. 이는 부모뿐아니라 부모의 부모클래스가 있다면 그 부모의 부모클래스의 프로퍼티와 메소드 역시 마찮가지다.
하지만, 부모가 아닌 형제 클래스(ex: Frog 클래스의 부모클래스(Amphibian)의 형제인 Mammal 클래스)의 프로퍼티나 메소드는 접근할 수 없다.

1.5.4 factory 와 지정 생성자

미리 정해진 프로퍼티를 포함하는 클래스의 특별한 메서드
혹시라도 디자인 패턴을 공부한 적이 있다면 factory pattern에 대해 알 것이다 만약, 해당 패턴을 잘 모르지만 관심이 있다면 여기로 가서 학습할 수 있다.
지정 생성자는 항상 클래스의 새 인스턴스를 반환하며 factory 메서드는 조금 더 유연하다.
class Energy { int joules; //기본 생성자 Energy(this.joules); //지정 생성자는 'Energy.'문법을 이용해 클래스의 인스턴스를 반환한다. Energy.fromWind(int windBlows) { final joules = _convertWindToEnergy(windBlows); return Energy(joules); } //factory는 기존 Energy 인스턴스를 반환할 수 있다. 혹은 새 인스턴스를 만들어 할당한 다음 반환한다. factory Energy.fromSolar(int sunbeams) { if (appState.solaEnergy != null) return appState.solaEnergy; final joules = _convertSunbeamsToJoules(sunbeams); return appState.solarEnergy = Energy(joules); } }
Dart
복사
디자인 패턴중 팰토리 메소드 패턴(Factory Method Pattern)과 유사하다.

1.5.5 열거자

상수 집합을 표현하는 특별한 클래스(enum)
enum Color { red, blue }
Dart
복사
문자열로 색을 구분해 그에 맞는 rgb 값을 반환하는 메소드가 있다고 할 때 enum을 이용하면 형식 안정성을 확보할 수 있다.
enum Color { red, green, blue } void updateColor(Color color) { switch (color) { case Color.red: //코드 break; case Color.blue: //코드 break; case Color.green: //코드 break; } } void main(){ updateColor(Color.red); updateColor(Color.blue); updateColor(Color.green); }
Dart
복사

1.6 정리

다트 문법은 C 언어를 기반으로 만들어진 언어와 유사하다.
다트는 객체지향의 엄격한 형식 언어다.
모든 다트 프로그램의 진입점은 main 함수다.
형식은 특정 상황에서 올바른 값을 할당하도록 코드를 강제한다. 이를 통해 코드의 안정성을 높힐 수 있다.
함수는 형식 또는 void를 반환해야 한다.
대부분의 연산자는 다른 연산자와 비슷하지만 ~/, is, as 와 같은 특별한 연산자도 있다.
어떤 값이 null인지 아닌지 확인할 때는 널 인지 연산자(null-aware)를 활용한다.
다트는 if/else, switch, 삼항 연산자 등의 제어 흐름을 제공한다.
switch문에 enum을 사용하여 enum의 모든 형식을 case로 확인하도록 컴파일러가 강제한다.
enum Color { red, green, blue } //Missing case clause for 'green'. //Try adding a case clause for the missing constant, or adding a default clause. switch (color) { case Color.red: //코드 }
Dart
복사
다른 언어와 비슷한 반복문을 지원한다(for, for-in, while, do-while)
다트는 고차함수를 사용해 함수를 값으로 전달 혹은 반환할 수 있다.
기본 생성자, factory 생성자, 지정 생성자 등 다양한 생성자를 지원한다.