제네릭 타입 특수화(Generic Type Specialization)
제네릭 타입 특수화(Generic Type Specialization)
제네릭을 정의해만 두면 쓸 수 없어요. 자리표시자를 실제 타입으로 채워서 **특수화(specialize)**해야 비로소 쓸 수 있는 타입이 돼요. 이 과정은 마치 저장해 둔 정의를 재생하면서 템플릿 자리표시자를 실제 타입 정의로 채우는 것과 같아요.
본문
일단 제네릭 타입이 정의되면, 그것을 이용해 다른 타입을 만들어 낼 수 있어요. 템플릿 자리표시자를 실제 타입 정의로 채워서 타입 정의를 '재생'하는 것과 같아요. 이 작업은 어떤 type 정의 블록에서든 할 수 있어요.
특수화된 타입은 아주 단순한 정의예요.
Specialized type
앞 절에서 선언한 TList를 기준으로, 아래는 유효한 타입 정의예요.
Type
TPointerList = specialize TList<Pointer>;
TIntegerList = specialize TList<Integer>;
Free Pascal 3.0 버전부터는 specialize 키워드를 변수 선언에서도 쓸 수 있어요.
Var
P : specialize TList<Pointer>;
specialize 키워드는 특수화된 타입의 일부예요. 그래서 완전히 정규화된 이름(full qualified name)을 쓸 때에는 specialize 키워드가 유닛 이름과 부모 타입 이름 뒤에 와야 해요. specialize 문 안의 타입은 알려져 있어야 해요. 다만 다른 제네릭 타입 정의 안에서는 예외예요.
두 제네릭 클래스 정의를 볼게요.
type
Generic TMyFirstType<T1> = Class(TMyObject);
Generic TMySecondType<T2> = Class(TMyOtherObject);
그렇다면 아래 특수화는 유효하지 않아요.
type
TMySpecialType = specialize TMySecondType<TMyFirstType>;
이유는 TMyFirstType이 제네릭 타입이라 완전히 정의되지 않았기 때문이에요. 컴파일러는 이렇게 불평해요.
Error: Generics cannot be used as parameters when specializing generics
하지만 아래는 허용돼요.
type
TA = specialize TMyFirstType<Atype>;
TB = specialize TMySecondType<TA>;
TB를 특수화할 때 TA는 이미 완전히 정의돼 있으니까요.
또한 specialize 키워드는 다른 제네릭 타입 정의 안에서도 쓸 수 있어요. 위 예시에서 본 것처럼요.
generic TList<_T>=class(TObject, specialize IList<_T>)
그리고
generic TPointSet<t> = array of specialize PlanarCoordinate<t>;
이런 정의에서 특수화는 제네릭 타입 자신이 특수화될 때만 수행되고, 그 시점에는 타입들이 알려져 있어요.
주석: 제네릭의 특수화인 클래스의 전방 정의(forward definition)는 만들 수 없어요. 즉 아래 코드는 컴파일되지 않아요.
TMyClass = Class;
// Other declarations
TMyClass = specialize TList<T>;
더 알아보기
- 제네릭 타입을 정의하는 방법은 8.2 절에서 다뤄요.
- 제네릭에 제약을 거는 방법은 8.4 절에서 확인할 수 있어요.
- specialize가 어떤 의미인지는 8.1 절(제네릭 소개)을 참고해요.