https://dart.dev/guides/language/language-tour
위 문서로 공부하며 기억해둘만한 내용만 정리
실습 사이트
부가적인 내용이나 다른 코드가 섞여있을 수 있습니다.
- Booleans
boolean 값을 나타내기 위해 bool 타입 사용.
bool 타입은 true와 false 두 타입만 있고, 컴파일 타임 상수.
bool test = true;
- Lists
Dart에서 배열은 List 객체.
대괄호 내에서 쉼표로 구분된다.
var list = [1, 2, 3,];
print(myList.length);
print(myList[0]);
List<int>로 추론된다. 따라서 정수가 아닌 객체를 삽입할 수 없다.
리스트의 끝에 쉼표를 추가해둘 수도 있다.
List의 인덱스는 0에서부터 시작한다.
컴파일 타임 상수인 List를 만들 수도 있다.
var myList = const [1, 2, 3,];
myList[0] = 3; // Uncaught Error: Unsupported operation: indexed set
Dart 2.3에서는 spread 연산자(...)와 널을 인식하는 spread 연산자(?...)를 도입했다.
여러 값을 컬렉션에 삽입하는 간결한 방법을 제공한다.
var list = [1, 2, 3];
var list2 = [0, ...list];
assert(list2.length == 4);
spread 연산자 우측의 표현식이 null일 수 있으면, ?...를 이용하여 예외를 피할 수 있다.
List<int>? list = null;
List<int> list2 = [0, ...?list];
print(list2); // [0]
Dart는 조건 또는 반복을 이용하여 컬렉션을 빌드하는 collection if와 collection for를 제공한다.
bool test = false;
var list = [1, 2, 3, if(test) 4];
print(list); // [1, 2, 3]. 조건에 따라 [1, 2, 3, 4]가 될 수도 있다.
var list = [1, 2];
var nextList = ['#0', for(var i in list) '#$i'];
print(nextList);
- Sets
Dart는 고유한 아이템의 순서 없는 컬렉션을 지원한다.
다트는 set 리터럴 {}과 Set 타입을 이용하여 set을 지원한다.
void main() {
var mySet = {'1', '2', '3'}; // Set<String> 추론. 잘못된 타입을 넣으면 에러가 발생한다.
var emptySet = <String>{};
Set<String> emptySet2 = {};
var emptySet3 = {}; // map을 생성한다. set이 아니다.
print(emptySet3.runtimeType); // JsLinkedHashMap<dynamic, dynamic>
}
Set도 리스트와 동일하게 spread 연산자, if, for를 지원한다.
- Maps
map은 키와 값을 연결하는 컬렉션이다. 키와 값은 모든 타입의 객체일 수 있다.
map 리터럴 {}와 Map 타입으로 지원한다.
var myMap = {1:'hi', 2:3};
print(myMap.runtimeType); // JsLinkedHashMap<int, Object>
var myMap2 = {1:'hi', 2:'3'};
print(myMap2.runtimeType); // JsLinkedHashMap<int, String>
map도 타입을 추론하고, 다른 타입을 넣으면 에러가 발생한다.
var myMap = Map<int, String>();
myMap[1] = '3';
myMap[4] = '5';
다음과 같이 생성할 수도 있다. Dart에서는 자바 등과 달리 new 키워드가 optional이다.
map에서 key를 찾을 수 없으면, null을 반환한다.
var myMap = {1 : 10};
print(myMap[2]); // null
컴파일 타임 상수 map을 만들기 위해, map 리터럴 앞에 const를 사용할 수 있다.
Map도 리스트와 동일하게 spread 연산자, if, for를 지원한다.
- Runes and grapheme clusters
유니코드 문자를 표현하기 위해 \uXXXX를 사용할 수 있다.
4글자 이상의 16진수로 표현하고 싶다면, \u{XXXX}를 사용할 수 있다.
개별 유니코드 문자를 읽고 싶다면, characters 패키지에 의해 String에 정의된 characters getter를 사용하면 된다.
- Symbols
Symbol 객체는 Dart 프로그램에 선언된 연산자 또는 식별자를 나타낸다.
식별자에 대한 symbol을 얻으려면, 식별자 앞에 symbol 리터럴 #을 사용하면 된다.
- Functions
Dart는 진정한 객체지향 언어이므로, 함수도 객체이고, Function이라는 타입을 가진다.
이는 함수가 변수로서 저장되거나 인자로 다른 함수에 넘겨질 수 있음을 의미한다.
Dart 클래스의 인스턴스를 마치 함수처럼 호출할 수도 있다.
bool foo(int num) {
return num > 2;
}
void main() {
var func = foo;
print(func(2));
}
다음과 같이 반환형을 생략할 수도 있다.
foo(int num) {
return num > 2;
}
다음과 같이 화살표 함수로 표현할 수도 있다.
bool foo(int num) => num > 2;
foo(int num) => num > 2;
var func = (num) => num > 2;
- Parameters
함수는 필요한 위치에 파라미터를 가질 수 있다.
이들 뒤에는 named 파라미터 또는 선택적인 위치 파라미터가 올 수 있다.
Named parameters : required로 명시되지 않는 한 선택적이다.
함수를 정의할 때, named parameter를 지정하기 위해 {param1, param2}를 사용한다.
bool foo({bool? one, bool? two}) {
return true;
}
void main() {
print(foo(two: true, one: false)); // 인자에 파라미터 명을 지정할 수 있다.
}
Tip : 매개변수가 optional이지만 null일 수 없는 경우, 디폴트 값을 지정하자.
named parameter는 optional parameter지만, required를 선언하여 사용자가 반드시 값을 제공해야함을 나타낼 수 있다.
bool foo({required bool? one, bool? two}) {
return true;
}
void main() {
print(foo(two: true));
}
Error: Required named parameter 'one' must be provided.
Optional positinal parameters : []에 함수 파라미터 세트를 래핑하면, optional positional parameters로 표시된다.
void foo(String hello, [String? world]) {
print(hello);
if(world != null) {
print(world);
}
}
void main() {
foo('hello');
foo('hello', 'world');
}
Default parameter values : =를 사용하여 선택적 파라미터(named, positional 모두)에 디폴트 값을 지정할 수 있다. 디폴트 값은 컴파일 타임 상수여야 한다. 디폴트 값이 제공되지 않으면, 디폴트 값은 null이 된다.
void foo([String? hello = 'hello', String? world]) {
print(hello); // hello
print(world); // null
}
void main() {
foo();
}
named parameter에서도 동일하게 사용할 수 있다.
=대신 :를 사용하는 코드는 deprecated 이므로 =를 사용하자.
디폴트 값으로 Map, List, Set 등의 컬렉션도 지정할 수 있다.
void foo([
List<int>? one = const [1, 2, 3],
Map<String, int>? two = const {"1":1}]) {
...
}
- main() function
앱의 진입점 역할을 하는 최상위 함수.
void 반환 타입, 선택적으로 인자에 List<String>을 가진다.
커맨드 라인 인자를 정의하고 파싱하기 위해 args 라이브러리를 사용할 수 있다.
- 일급 객체로서의 함수
함수를 다른 함수의 파라미터로 전달할 수 있다.
함수를 값에 넣을 수 있다.
void foo(var bar) {
bar();
}
void main() {
var func = () => 1;
foo(func);
foo(() => 2);
}
- Anonymous functions(익명 함수)
대부분의 함수는 이름을 가지지만, Anonymous function 이라고 하는 이름 없는 함수(lambda 또는 closure)를 만들 수 있다.
익명 함수는 변수에 할당할 수 있고, 컬렉션에 넣거나 제거할 수 있다.
([[Type] param1[, …]]) {
codeBlock;
};
void main() {
var list = [1, 2, 3];
list.forEach((item) {
print(item);
});
}
타입없는 파라미터로 익명 함수를 정의한 예시다.
var list = [1, 2, 3];
list.forEach((item) => print(item));
하나의 표현식이거나 반환문만 있는 경우, 화살표 표기를 이용할 수도 있다.
- Lexical scope
Dart는 변수의 범위가 코드 레이아웃에 의해 정적으로 결정된다.
중첩된 함수 내에서는 상위 레벨에 있는 변수를 사용할 수 있다.
- Lexical closures
closure는 함수가 원래 범위 외부에서 사용되는 경우에도 변수에 접근할 수 있는 함수 객체다.
함수는 주변 범위에 정의된 변수를 닫을 수 있다.
Function add(int addBy) {
return (i) => i + addBy;
}
void main() {
var addTwo = add(2);
var addFour = add(4);
print(addTwo(1)); // 3
print(addFour(1)); // 5
}
파라미터로 받은 addBy를 캡쳐하고, 반환된 함수는 이를 기억한다.
- Testing functions for equailty
최상위 함수, 스태틱 메소드, 인스턴스 메소드에 대한 equality를 확인해본다.
void baz() { }
class A {
static void foo() { }
void bar() { }
}
void main() {
var x = A();
var y = A();
print(A.foo == A.foo); // true
print(x.bar == y.bar); // false
var z = baz;
print(z == baz); // true
}
서로 다른 인스턴스인 경우, 인스턴스 메소드 equality가 false로 나타난다.
- 반환값이 지정되지 않으면, 함수 바디에 return null;이 암시적으로 추가된다.
foo() { }
void main() {
print(foo()); // null
}
- Operators
https://dart.dev/guides/language/language-tour#operators
type test operators : as, is, is! 연산자는 런타임에 타입을 확인하는데 편리하다.
is : obj가 T로 지정된 인터페이스 T를 구현한 경우, obj is T는 true이다. 예를 들어, obj is Object?는 항상 true.
as : 객체가 해당 타입이 확실한 경우에 객체를 해당 타입으로 캐스팅한다.
객체가 T 타입인지 확실하지 않으면, 객체를 사용하기 전에 is 를 이용하여 확인하자.
as에서 타입이 다르면 예외를 던진다.
??= : 대상 변수가 null인 경우에만 할당하려면 사용한다.
void main() {
int? a = 1;
int? b = 3;
b ??= a;
print(b); // 3
}
void main() {
int? a = 1;
int? b = null;
b ??= a;
print(b); // 1
}
compound assignment operator는 operation과 assignment를 결합한다.
=, +=, /=, >>>=, ...
- Conditional expressions
condition ? expr1 : expr2
condition이 true면 expr1, false면 expr2가 실행 또는 반환된다.
void main() {
var cond = false;
var val1 = cond ? '1' : 2;
print(val1.runtimeType); // int
var val2 = !cond ? '1' : 2;
print(val2.runtimeType); // String
}
표현식에 서로 다른 타입을 넣을 수도 있었다.
expr1 ?? expr2
expr1이 null이 아니면 expr1을 실행 또는 반환하고, null이면 expr2를 실행 또는 반환한다.
String foo(String? str) => str ?? 'default';
void main() {
print(foo('string')); // string
print(foo(null)); // default
}
- Cascade notiation
Cascade(.., ?..) 를 사용하면 동일한 객체에 대해 일련의 작업을 수행할 수 있다.
인스턴스 멤버에 접근하는 것 외에도, 동일한 객체에서 인스턴스 메소드를 호출할 수 있다.
이를 통해 임시 변수 생성 단계를 줄이고, 보다 유동적인 코드를 작성할 수 있다.
var paint = Paint()
..color = Colors.black
..strokeCap = StrokeCap.round
..strokeWidth = 5.0;
생성자 Paint()는 Paint 객체를 반환한다.
cascade 표기를 한 코드는, 반환될 수 있는 값을 무시하며 객체에 대해서 동작한다.
위 코드는 아래 코드와 동일하다.
var paint = Paint();
paint.color = Colors.black;
paint.strokeCap = StrokeCap.round;
paint.strokeWidth = 5.0;
cascade 동작하는 코드가 null일 수 있는 경우, 첫 연산자에 대해 ?..를 사용하면 된다.
?..로 시작하면, null 객체에 대해 cascade 연산이 수행되지 않음을 보장한다.
querySelector('#confirm') // Get an object.
?..text = 'Confirm' // Use its members.
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'))
..scrollIntoView();
위 코드는 아래 코드와 동일하다.
var button = querySelector('#confirm');
button?.text = 'Confirm';
button?.classes.add('important');
button?.onClick.listen((e) => window.alert('Confirmed!'));
button?.scrollIntoView();
?.. 문법은 2.12버전 이상에서 제공된다.
cascade를 중첩해서 사용할 수도 있다.
final addressBook = (AddressBookBuilder()
..name = 'jenny'
..email = 'jenny@example.com'
..phone = (PhoneNumberBuilder()
..number = '415-555-0100'
..label = 'home')
.build())
.build();
실제 객체를 반환할 때 cascade를 구성하도록 주의하자.
다음 코드에서 sb.write()의 반환형은 void이기 때문에 실패한다.
var sb = StringBuffer();
sb.write('foo')
..write('bar'); // Error: method 'write' isn't defined for 'void'.
엄밀히 말하면 .. 표기법은 연산자가 아니라 Dart 구문의 일부일 뿐이다.
- Other operators
() : 함수 호출
[] : 재정의 가능한 [] 연산자 호출을 나타낸다. 예를 들어, fooList[1]은 int 1을 fooList에 전달하여 인덱스 1번 요소에 접근한다.
?[] : []와 비슷하지만, []의 가장 왼쪽 피연산자는 null일 수 있다. 예를 들어, fooList[1]은 fooList가 null이 아닌 경우 int 1을 fooList에 전달하여 인덱스 1번 요소에 접근한다. (fooList가 null이면, 표현식은 null로 평가된다.)
. : 표현식의 속성을 참조한다. 예를 들어, foo.bar는 표현식 foo의 bar 속성을 선택한다.
?. : .와 비슷하지만, .의 가장 왼쪽 피연산자는 null일 수 있다. 예를 들어, foo?.bar는 foo가 null이 아닌경우 표현식 foo의 bar 속성을 선택한다. (foo가 null이면, 표현식을 null로 평가된다.)
! : nullable하지 않은 형식으로 표현식을 캐스팅하고, 캐스팅이 실패하면 런타임 예외를 던진다. 예를 들어, foo!.bar는 foo가 null이 아님을 단언하고, foo가 null이 아니라면 bar 속성을 선택한다. foo가 null이면 런타임 예외가 발생한다.
'플러터' 카테고리의 다른 글
Dart 문법 공부 - 6 (0) | 2022.06.01 |
---|---|
Dart 문법 공부 - 5 (0) | 2022.05.30 |
Dart 문법 공부 - 4 (0) | 2022.05.29 |
Dart 문법 공부 - 3 (0) | 2022.05.29 |
Dart 문법 공부 - 1 (0) | 2022.05.28 |