< Dart Deep Dive > - Generic(제네릭) 파헤쳐보기
이번 글은 Generics (<…>) 에 대해 이해해보도록 하겠습니다.
Map의 API 공식문서를 보면 Map<K, V>라고 되어있습니다. 이처럼 <…> 로 Map의 제네릭 타입을 표시해서 배열 안에 들어갈 변수의 타입을 지정할 수 있어요.
제네릭을 쓰는 이유는?
제네릭은 보통 배열 내 데이터들의 타입을 명확히 하기 위해 필요로 하는데, 또 다른 두 가지 이점이 있습니다.
- 제네릭 유형을 잘 지정하면 더 나은 코드를 만들 수 있고
- 코드 중복을 줄일 수 있습니다.
만약 문자열로만 이뤄진 List을 만들고 싶다면 List< String> 이렇게 선언합니다. 그럼 개발자 동료나 Dart Pad가 이 List에는 문자열말고 다른 타입의 데이터는 입력할 수 없음을 알 수 있습니다. 여기 예시가 있습니다.
var names = <String>[];
names.addAll(['ChangHwan', 'SeokHyun', 'YeSeul']);
names.add(3); // 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);
}
//T는 아직 데이터 유형이 정해지지 않은 것으로 나중에 우리가 정의할 수 있음.
abstract class Cache<T> {
T getByKey(String key);
void setByKey(String key, T value);
}
collection literals 사용하기
배열을 만들 때 제네릭을 지정함으로서 타입을 명확히 할 수 있습니다.
//List: <Type>[ ]
var names = <String>['ChangHwan', 'SeokHyun', 'YeSeul'];
//Set: <Type>{ }
var uniqueNames = <String>{'ChangHwan', 'SeokHyun', 'YeSeul'};
//Map: <Type> { : }
var pages = <String, String>{
'Member 1': 'ChangHwan',
'Member 2': 'SeokHyun',
'Member 3': 'YeSeul'
};
생성자와 제네릭 사용하기
생성자를 사용할 때도 < > 안에 데이터 Type을 지정함으로서 배열을 생성할 수 있어요.
예시1) Set(E).from(Iterable elements) 생성자
var memberName = Set<String>.from(names);
예시2) Map<K, V> ( ) 생성자
var Hack_up = Map<String, String>( );
배열이 담고 있는 타입이 무엇인지 알 수 있습니다.
var names = <String>[];
names.addAll(['ChangHwan', 'SeokHyun', 'YeSeul']);
print(names is List<String>); // true
공식 문서에 따르면 Java 언어에서는 어떤 객체가 리스트인지 아닌지의 여부만 테스트가 가능하고 List
파라미터(인수)의 타입을 제한하기
클래스 상속과 비슷한 개념인데요, 간혹 인수로 받을 데이터의 타입을 제한하고 싶을 때가 있습니다. 이 때 저희가 제네릭을 지정하게 되면 그 인수는 저희가 지정한 데이터 타입의 하위 형식이어야 하는데요, 이는 ‘extends’ 키워드로 구현할 수 있습니다.
흔히 사용하는 사례로는 어떤 type을 객체(Object)의 하위 유형으로 만들어 null을 사용하는 것을 제한할 수 있습니다.
class Foo<T extends Object> {
// 클래스 Foo에는 null이 아닌 모든 유형의 타입을 쓸 수 있음.
}
메서드나 함수에 Generic 사용하기
메서드(Methods)나 함수(Functions)의 인자(arguments)에도 제네릭 사용을 쓸 수 있습니다.
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;
}
위 예제에서 여러 위치에서 Generic type을 사용할 수 있는 것을 볼 수 있습니다.
- 함수의 결과값의 타입도 T
- 인수의 타입이 ‘List
' - 지역 변수 tmp의 타입도 T
여기서 T는 아직 데이터 타입이 정해지지 않은 것으로 나중에 여러분이 정의할 수 있습니다.
여기까지 제네릭에 대해서 파헤쳐봤는데요 !
확실히 하나 이상의 데이터를 다룰 때, 데이터 타입 별로도 잘 정리할 수 있고 클래스, 함수, 배열 등의 제네릭을 보면서 거기에 사용된 데이터 타입을 유추할 수 있다는 점에서도 유용한 것 같습니다.
부족한 글 읽어주셔서 감사합니다:)
댓글남기기