https://dart.dev/guides/language/language-tour
A tour of the Dart language
A tour of all the major Dart language features.
dart.dev
위 문서로 공부하며 기억해둘만한 내용만 정리
DartPad
dartpad.dev
실습 사이트
부가적인 내용이나 다른 코드가 섞여있을 수 있습니다.
- Generics
기본 배열 타입인 List에 대한 API 문서를 보면, 타입이 실제로 List<E>인 것을 확인할 수 있다.
<...> 표기는 List를 제너릭(또는 parameterized) 타입, 즉 formal type parameters가 있는 유형으로 표시한다.
관례에 따라 대부분의 타입 변수는 E, T, S, K, V와 같은 단일 문자 이름을 사용한다.
- 왜 제너릭을 사용?
제너릭은 종종 타입 안전성에 필요하지만, 단지 코드 실행을 허용하는 것보다 더 많은 이점이 있다.
제너릭 타입을 적절하게 지정하면 더 나은 코드가 만들어지고, 코드 중복을 줄일 수 있다.
리스트에 오직 문자열만 넣기를 원하는 경우, List<String>으로 선언할 수 있다.
이를 통해 실수로 리스트에 문자열이 아닌 값을 넣는 것을 동료 또는 도구가 감지해낼 수 있다.
var names = <String>[];
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // Error
또 다른 이유는, 코드의 중복을 줄일 수 있다.
제너릭은 정적 분석을 계속 활용하면서, 단일 인터페이스 및 구현을 여러 타입 간에 공유할 수 있다.
다음과 같이 객체 캐싱을 위한 인터페이스를 만들어보자.
abstract class ObjectCache {
Object getByKey(String key);
void setByKey(String key, Object value);
}
문자열에 특화된 버전을 만드려면, 또 다른 인터페이스를 만들 수도 있다.
abstract class StringCache {
String getByKey(String key);
void setByKey(String key, String value);
}
또는, 숫자에 특화된 인터페이스를 만들어야 할 수도 있다.
제너릭 타입은 이런 문제를 해결할 수 있다.
타입 파라미터를 가지는 단일 인터페이스를 만들면 된다.
abstract class Cache<T> {
T getByKey(String key);
void setByKey(String key, T value);
}
T는 나중에 개발자가 정의한 타입으로 대체될 수 있다.
- 컬렉션 리터럴 사용
List, Set, Map은 parameterized 될 수 있다.
Parameterized 리터럴은 앞서 본 리터럴과 유사하다.
여는 괄호 앞에 <타입> 또는 <키 타입, 값 타입>을 추가해주면 된다.
var names = <String>['Seth', 'Kathy', 'Lars'];
var uniqueNames = <String>{'Seth', 'Kathy', 'Lars'};
var pages = <String, String>{
'index.html': 'Homepage',
'robots.txt': 'Hints for web robots',
'humans.txt': 'We are people, not machines'
};
- 생성자에 parameterized type 사용
생성자에 하나 이상의 타입을 지정하려면, 클래스 이름 뒤에 타입을 <...>로 묶어준다.
var nameSet = Set<String>.from(names);
var views = Map<int, View>();
- 제너릭 컬렉션과 컬렉션에 포함된 타입
Dart 제너릭 유형은 구체화되어서 런타임에 타입 정보를 전달한다.
var names = <String>[];
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
- Parameterized type 제한하기
제너릭 타입을 구현할 때, 인자로 제공되는 타입을 특정 타입의 서브타입으로 제한하고 싶을 수 있다.
extends 키워드를 이용하면 된다.
일반적인 사용 사례는 Object의 하위 타입(디폴트인 Object? 대신)으로 만들어서 null을 허용하지 않도록 하는 것이다.
class Foo<T extends Object> {
// Any type provided to Foo for T must be non-nullable.
}
Object 외에도 extends를 사용할 수 있다.
다음 코드는 SomeBaseClass를 확장해서, T 타입 객체에서 SomeBaseClass의 멤버가 호출될 수 있다.
class Foo<T extends SomeBaseClass> {
// Implementation goes here...
String toString() => "Instance of 'Foo<$T>'";
}
class Extender extends SomeBaseClass {...}
SomeBaseClass 또는 SomeBaseClass의 하위 타입을 인자로 사용할 수 있다.
var someBaseClassFoo = Foo<SomeBaseClass>();
var extenderFoo = Foo<Extender>();
제너릭 인자를 지정하지 않을 수도 있다.
var foo = Foo();
print(foo); // Instance of 'Foo<SomeBaseClass>'
SomeBaseClass가 아닌 타입을 지정하면 에러가 발생한다.
var foo = Foo<Object>(); // error
- 제너릭 메소드 사용하기
초기의 Dart의 제너릭은 클래스에만 지원됐다.
제너릭 메소드라고 불리는 새로운 문법에서는, 함수와 메소드에도 타입 인자를 지원한다.
T first<T>(List<T> ts) {
// Do some initial work or error checking, then...
T tmp = ts[0];
// Do some additional checking or processing...
return tmp;
}
- Libraries and visibility
import와 library 지시문은 모듈식의 공유 가능한 코드 베이스를 만들 수 있게 도와준다.
라이브러리는 API를 제공할 뿐만 아니라, 정보 보호의 단위가 된다.
_로 시작하는 식별자는 오직 라이브러리 내에서만 볼 수 있다.
모든 Dart 앱은 library 지시문을 사용하지 않더라도 라이브러리다.
라이브러리는 packages를 이용하여 분산될 수 있다.
- 라이브러리 사용하기
import를 사용하여, 한 라이브러리의 네임스페이스가 다른 라이브러리의 범위에서 사용되는 방식을 지정한다.
일반적으로 Dart 웹 앱은, 다음과 같이 가져올 수 있는 dart:html 라이브러리를 사용한다.
import 'dart:html';
라이브러리를 지정하는 URI가 인자로 사용된다.
내장 라이브러리를 위해, URI는 특별한 dart: 스킴을 가진다.
다른 라이브러리를 위해, 파일 시스템 경로 또는 package: 스킴을 사용할 수 있다.
package: 스킴은 pub tool과 같은 패키지 매니저에 의해 제공되는 라이브러리를 지정한다.
import 'package:test/test.dart';
- 라이브러리 prefix 지정하기
만약 식별자 충돌이 일어나는 2개의 라이브러리를 가져오고 싶다면, 라이브러리에 prefix를 지정할 수 있다.
예를 들어, library1과 library2는 모두 Element 클래스를 가지는 상황을 보자.
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
// Uses Element from lib1.
Element element1 = Element();
// Uses Element from lib2.
lib2.Element element2 = lib2.Element();
- 라이브러리 일부만 가져오기
라이브러리의 일부만 가져오고 싶다면, 선택적으로 가져올 수도 있다.
// Import only foo.
import 'package:lib1/lib1.dart' show foo;
// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;
- 라이브러리 지연 로딩(Lazily loading)
지연 로딩을 사용하면 라이브러리가 필요한 경우에 웹 앱이 요청하여 가져올 수 있다.
초기 시작 시간을 줄이거나, A/B 테스팅을 수행하거나, 드물게 사용되는 기능을 가져오기 위해 사용될 수 있다.
지연 로딩을 사용하려면, 먼저 deferred as를 사용하여 라이브러리를 가져와야 한다.
라이브러리가 필요할 때, 라이브러리 식별자로 loadLibrary()를 호출한다.
Future<void> greet() async {
await hello.loadLibrary();
hello.printGreeting();
}
위 코드에서 await 키워드는 라이브러리를 가져올 때 까지 실행을 멈춘다.
loadLibrary()는 여러 번 호출해도 오직 한 번만 가져온다.
지연 로딩을 사용할 때 다음 사항을 염두에 두자.
지연된 라이브러리의 상수는, 가져오는 파일에서는 상수가 아니다. 지연된 라이브러리가 로드될 때 까지 상수는 존재하지 않는다.
가져오는 파일에서 지연된 라이브러리의 타입을 사용할 수 없다. 지연된 라이브러리와 가져오는 파일 모두에서 가져온 라이브러리로, 인터페이스 타입 이동을 고려하자.
Dart는 deferred as namespace를 사용하여 정의한 네임스페이스에, loadLibrary()를 암시적으로 삽입한다. loadLibrary()는 Future를 반환한다.
'플러터' 카테고리의 다른 글
Flutter - don't support null safety (0) | 2022.06.01 |
---|---|
Dart 문법 공부 - 6 (0) | 2022.06.01 |
Dart 문법 공부 - 4 (0) | 2022.05.29 |
Dart 문법 공부 - 3 (0) | 2022.05.29 |
Dart 문법 공부 - 2 (0) | 2022.05.28 |