개요
제네릭이란?
- 제네릭(generics)은 Dart에서 사용되는 강력한 기능 중 하나이다.
- Collection을 정의할 때 많이 활용된다.
- < ... > 괄호를 활용하여 그 안에 타입을 지정하는 것을 제네릭이라고 한다.
(정식 유형 매개 변수가있는 타입)
- 관례상 대부분의 타입 변수는 E, T, S, K, V 같은 single-letter 이름을 가진다.
- List 혹은 List 이렇게 사용하면서 리스트에 String 타입만 담을 것인지 , int 타입만 담을 것인지를 정할 수 있는데 여기서 이미 제네릭을 사용한 것이다.
- List, Set, Map 같은 collection 선언시에도 Generics 표현에 의해 타입이 명확해 진다.
- 코드
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'
};
사용이유
- 제네릭은 코드의 재사용성과 가독성을 향상시키고 타입 안정성(Type Safety)을 보장하는 데 도움을 준다.
- 따라서 Dart에서는 제네릭을 사용하여 유연하고 안전한 코드를 작성할 수 있다.
- 제네릭은 함수나 클래스의 인수, 반환 값, 변수의 타입을 일반화(generalize)하는 방법을 제공한다.
- 일반적인 데이터 타입 대신에 타입 매개변수(type parameter)를 사용하여 여러 위치에서 동일한 코드를 재사용할 수 있다.
- 제네릭을 사용하면 데이터 타입에 대한 추상화를 가능하게 한다.
- 예를 들어, List를 생각해보면, 제네릭을 사용하지 않을 경우 다양한 데이터 타입을 담을 수 없다. 하지만 제네릭을 사용하면 다양한 타입의 리스트를 생성할 수 있다.
- 이렇게 제네릭을 사용하면 컴파일러가 타입 체크를 수행하므로 타입 안정성을 보장할 수 있다.
- 여러가지 데이터 타입에 대해 동일한 동작을 해야 하는 코드가 있다면, 데이터 타입별로 코드를 생성할 필요 없게되어 코드를 줄일 수 있다.
- 코드
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);
}
- 성능적인 이유
- 데이터 타입에 따라 컴퓨터가 할당하는 메모리 용량에는 차이가 있다.
- 데이터 타입을 지정하지 않고 코딩한다면 컴퓨터가 타입을 지정했을때보다 많은 메모리를 할당해야하고 프로그램의 질 하락으로 이어질 가능성도 부정할 수는 없다고 봐야한다.
사용방법
- Dart에서 제네릭을 사용하려면 클래스나 함수를 정의할 때 타입 매개변수를 사용해야 한다.
- 타입 매개변수는 일반적으로 대문자로 표시되며, 함수나 클래스 내부에서 해당 타입을 사용할 수 있다.
- 여러 타입의 값을 파라미터로 받는 constructor 를 하나의 함수로 구현할 수 있다.
- 코드1
Set<E>.from(Iterable elements) constructorvar nameSet = Set<String>.from(names);
var ageSet = Set<int>.from(ages);
- 코드2
Map<K, V>() constructorvar views = Map<int, View>();
var views = Map<double, String>();
- generic 타입을 적용할 때 클래스의 파라미터의 타입을 제한할 수 있다.
- generic은 method, function 의 파라미터에도 적용할 수 있다.
- 함수의 리턴타입, argument 의 타입, 내부 변수의 타입에 generic type 이 쓰였다.
주의점
- dynamic같은 어떤 형태나 올 수 있는 제네릭은 지양하는 것이 좋다.
- 들어오는 데이터가 정리가 제대로 안되어 dynamic말고는 해결책이 없는 상황에서만 사용하는 것이 좋다.
기타
- Dart의 generic타입들은 Java 와 다르게 런타임 동안 타입의 정보가 없어지지 않는다.
예제
void main() {
Lecture<String, String> lecture1 = Lecture(
'123',
'lecture1',
);
lecture1.printIdType();
Lecture<int, String> lecture2 = Lecture(
123,
'lecture2',
);
lecture2.printIdType();
}
class Lecture<T, X> {
final T id;
final String name;
Lecture(
this.id,
this.name,
);
void printIdType() {
print(id.runtimeType);
}
}