Dart : Generics
Dart 의 Generics 에 대해 살펴보자.
Dart 의 API documentation 에서 데이터 타입이나 클래스의 정의를 보면 <> 와 그 안에 'E, T, S, K, V' 같은 문자로 된 표현을 볼 수 있다.
Generics 를 사용하면 변수 타입을 명확히 표현할 수 있다는 장점과 반복되는 코드를 줄일 수 있다는 장점이 있다.
List<String> 식의 표현으로 string 타입의 값을 가지는 리스트라는 명확한 선언을 통해 잘못된 타입의 값 삽입을 막을 수 있다.
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
names.add(42); // Error
여러가지 데이터 타입에 대해 동일한 동작을 해야 하는 코드가 있다면, 데이터 타입별로 코드를 생성할 필요 없게되어 코드를 줄일 수 있다.
// Object specific class
abstract class ObjectCache {
Object getByKey(String key);
void setByKey(String key, Object value);
}
// String specific class
abstract class StringCache {
String getByKey(String key);
void setByKey(String key, String value);
}
// Generic type
abstract class Cache<T> {
T getByKey(String key);
void setByKey(String key, T value);
}
Using collection literals
List, Set, Map 같은 collection 선언시에도 Generics 표현에 의해 타입이 명확해 진다.
List : [] -> <type>[]
Set : {} -> <type>{}
Map : { : } -> <type, type>{ : }
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'
};
Using parameterized types with constructors
여러 타입의 값을 파라미터로 받는 constructor 를 하나의 함수로 구현할 수 있다.
예1) Set<E>.from(Iterable elements) constructor
var nameSet = Set<String>.from(names);
var ageSet = Set<int>.from(ages);
예2) Map<K, V>() constructor
var views = Map<int, View>(); // integer keys and values of type View
var views = Map<double, String>(); // double keys and string values
Generic collections and the types they contain
Dart 의 generic 타입들은 Java 와 다르게 런타임 동안 타입의 정보가 없어지지 않는다.
자세한것은 Java 와 Dart의 공식 문서를 참조.
In contrast, generics in Java use erasure, which means that generic type parameters are removed at runtime. In Java, you can test whether an object is a List, but you can’t test whether it’s a List<String>.
var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
print(names is List<String>); // true
Restricting the parameterized type
generic 타입을 적용할 때 클래스의 파라미터의 타입을 제한할 수 있다.
extends 를 사용해 subclass 로 제한하는 방법이다.
class A {
hello() => print('class A');
}
class B extends A {
hello() => print('class B');
}
class C<T extends A> {
hello() => print('class C ${T.runtimeType}');
}
class D {
hello() => print('class D');
}
void main() {
var a = C<A>();
var b = C<B>();
var c = C();
// var d = C<D>(); // error ('D' doesn't extend 'A'.)
}
Using generic methods
generic 이 클래스에만 적용할 수 있었는데 method, function 의 파라미터에도 적용할 수 있다.
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;
}
위 예제를 보면 함수의 리턴타입, argument 의 타입, 내부 변수의 타입에 generic type 이 쓰였다.