다트 프로그램 내부
- 모든 다트 프로그램은 main 함수를 반드시 정의해야 한다.
- 모든 변수는 형식을 가져야 하며, 형식(or void)를 반환해야 한다.
입출력과 다트 라이브러리
- 다양한 라이브러리를 제공하지만, dart:core 라이브러리만 자동 로드된다.
- dart:html, dart:async, dart:math 등 다양한 라이브러리를 사용할 수 있다.
- dart:io를 활용하여 서버 응용프로그램이나 명령줄 응용프로그램을 구현할 수 있다.
import 'dart:io';
void main() {
stdout.writeln('Greet Somebody');
String? input = stdin.readLineSync();
return helloDart(input);
}
void helloDart(String? name){
if (name == null) return;
print('Hello, $name');
}
다트 프로그래밍 개념
- 다트는 객체지향 언어이며, 단일 상속을 지원한다.
- 모든 객체는 Object 클래스를 상속받는다.
- 다트는 형식을 가진다.
- 문자열을 반환한다고 선언한 함수에서 숫자를 반환할 수 없다.
- 최상위 수준 함수와 변수를 지원하며 이를 라이브러리 멤버라고 부른다.
형식을 갖는 프로그래밍 언어
- 변수의 형식을 컴파일 타임에서 알 수 있거나 추론할 수 있다면 형식을 갖는 언어이다.
- 런타임에서 형식을 추론한다면 이는 동적 언어이다.
- 형식을 사용하는 코드는 일반적으로 안전하다.
- 다트는 컴파일 타임에 형식을 확인한다.
다트의 형식 시스템
- 다트는 변수를 선언할 때 형식을 지정해야 한다.
- dynamic을 사용하면 동적 형식도 활용 가능하며, 컴파일러가 해당 변수에 모든 형식을 허용한다.
// 선택적 형식 선언
dynamic myNumber = 'Hello';
Map<String, dynamic> json = Map();
- var 키워드는 코드 형식에 도움을 주는 키워드이다.
- 범위가 한정적이며, 어떠한 변수형도 받을 수 있지만 변경할 수 없다.
var boo = 123;
변수와 할당
- 일반적으로 다트에서는 값을 할당하지 않으면, null을 값으로 가진다.
- 스타일 가이드에서는 객체에 null을 할당하지 않는 것을 추천한다.
final, const, static
- 위 세 키워드는 변수의 형식을 확장한다.
- [final] : 한 번만 할당할 수 있으며, 클래스 수준에서 변수를 할당하기 전에 선언한다.
- 클래스의 생성자에서 할당하는 모든 변수에 final을 사용한다.
- [const] : 할당하기 전에 선언하지 않는다.
- 컴파일 이후로 항상 같은 값을 가지는 상수를 선언하며, 성능이 개선된다.
const String name = 'Nora';
널 인지 연산자
- 다트에서는 널 인지 연산자로 null Safe 환경을 지원한다.
- null이면 오류를 발생하지 않고, 아무것도 하지 말라고 지시한다.
?. 연산자
- null이 아니라면 할당하고, null이면 오류 없이 null을 할당하라는 의미의 연산자이다.
class User {
int? age;
User(this.age);
}
void main() {
User? user = User(25);
print(user?.age);
}
?? 연산자
- 값이 존재하지 않는 상황에 할당할 백업값을 지정할 수 있다.
- 변수가 null이라면 백업값을 활용하라는 의미이다.
int? age;
int defaultAge = 18;
int userAge = age ?? defaultAge ;
print(userAge);
??= 연산자
- ??와 비슷하지만 반대의 작업을 수행한다.
- null일 때만 백업값을 할당한다.
// ??=
int? number;
number ??= 10;
print(number); //10
number ??= 20;
print(number); // 10
제어 흐름
- 다트는 대부분 고수준 언어와 마찬가지로 제어 흐름 기능을 사용한다.
void main(){
// if - else
bool isTrue = false;
if(isTrue && isTrue || isTrue){
print(true);
}else{
print(false);
}
// switch - case
int number = 1;
switch(number){
case 0:
print('zero');
break; //break를 수행하지 않으면 모든 case 수행
default:
print("edefault");
}
}
삼항 연산자
- 삼항 연산자로 if/else를 대신할 수 있다.
//ternary
int? name;
var nametag = name == null ? "null" : "username";
다트와 함수
- 함수 시그니처는 [반환 형식] [함수명](인수 형식arg)의 패턴으로 구성된다.
- 다트는 함수를 인수로 전달하거나 함수에서 함수를 반한하는 고차 함수를 지원한다.
- 함수 바디에 하나의 행의 코드가 있을 때 단축 표현식을 활용할 수 있다.
//단축 표현식 : 함수 바디에 한 행의 코드를 구현
String makeGreeting(String name) => 'Hello, $name';
이름 지정 파라미터
- 이름 지정이란 함수를 호출할 때 인수를 레이블과 쌍으로 제공한다는 의미이다.
//이름 지정
void debugger({required String message,required int lineNum}) {
debugger(message:'A',lineNum:44);
}
- 중괄호로 이름 지정 파라미터를 감싸고, required 키워드로 반드시 이름을 지정하도록 정의한다.
고급 함수 개념
- 함수는 코드 재사용의 핵심이며, 고차 함수를 이용해 코드의 추상 계층을 추가하면 이해하기 쉬운 코드를 만들 수 있다.
- 함수를 쪼개서 자신만의 기능을 구현하는 행위를 추상화라고 한다.
- 고차 함수를 활용하면 로직을 추상화 할 수 있고, 함수를 인수로 받거나 함수를 반환할 수 있다.
//고차 함수와 추상화
List<int> nums = [1,2,3];
void printNum(int num) => print(num);
void printNumbers(List<int> nums){
nums.forEach(printNum);
}
- forEach와 같은 Iterable 객체는 모든 멤버에게 고차 함수를 제공한다.
다트의 객체지향 프로그래밍
- 요즘 앱은 큰 데이터셋을 처리할 수 있도록 돕는 역할을 주로 수행한다.
- 모든 앱은 사용자가 데이터와 쉽게 상호작용할 수 있도록 돕는다.
- 데이터는 현실을 반영함, 컴퓨터가 우리가 원하는 작업을 수행하도록 데이터에 추상화를 추가해야 한다.
- 이는 읽고, 이해하기 쉬우며 재사용성 높은 코드를 제공한다.
- 모든 클래스는 현실의 사물을 표현할 수 있다.
생성자
- 새 인스턴스를 만들 때 수행할 동작을 지정할 수 있는데, 이 역할을 하는 함수를 생성자라고 한다.
- 기본 생성자는 클래스와 같은 이름을 갖는다.
- 함수로 전달하려는 인수(클래스의 프로퍼티로 할당하려는 값)는 함수의 인수처럼 정의한다.
class Animal {
String name;
String type;
Animal(this.name, this.type);
}
상속
- 한 클래스가 다른 클래스를 상속받거나 다른 클래스의 슈퍼 클래스가 될 수 있다.
class Animal {
late String name;
late String type;
Animal();
}
class Cat extends Animal {
Cat();
}
void main(){
Cat cat = Cat();
cat.name = 'name';
cat.type = '4';
}
factory와 지정 생성자
- factory와 지정 생성자는 항상 클래스의 새 인스턴스를 반환한다.
- factory 메서드는 캐시된 인스턴스 또는 서브 형식의 인스턴스를 반환하기 때문에, 조금 더 유연하다.
class Energy {
late int power;
Energy(this.power); //기본 생성자
//지정 생성자 : 새로운 객체를 항상 생성
//여러 개의 생성자 제공
Energy.fromWind(int windBlows)
: power = windBlows*2;
// private 생성자
Energy._internal(this.power);
//싱글톤 객체
static final Energy _instance= Energy._internal(0);
//factory 생성자 : return을 사용해 새로운 객체 또는 기존 객체 반환
//싱글톤 패턴, 캐싱, JSON 변환 등에 사용
// 기존 객체 재사용
factory Energy.singleton(){
return _instance;
}
}
열거자
- 상수 집합을 표현하기 위해 enum(열거 클래스)를 사용할 수 있다.
enum Color{red,blue}
void updateColor(Color color){
switch(color){
case Color.red:
print("red");
case Color.blue:
print("blue");
}
}