2006/08/28

Curiously Recurring Generics

在《The Java Programming Language, 4/e》p.159 的註腳 3 這麼說:

Enum is actually a generic class defined as Enum<T extends Enum<T>>. This circular definition is probably the most confounding generic type definition you are likely to encounter. We're assured by the type theorists that this is quite valid and significant, and that we should simply not think about it too much, for which we are grateful.

此書作者之一 Ken Arnold 在他的一篇 blog《Generics Considered Harmful》引用這段文字,然後說:

And we are grateful. But if we (meaning David)(JK 注:David Holmes,另一位協同作者,負責 Generics 章節)can't explain it so programmers can understand it, something is seriously wrong.

在我看來,這雖然不是 Java Generics 最令人頭疼的部份,也夠令人頭疼了。今日翻《Thinking in Java, 4/e》,發現若從 C++ CRTP(Curiously-Recurring Template Pattern)類推,解釋變得相當簡單,描述如下。

C++ CRTP 指的是一個 class C 繼承自某個 class template 具現體,並以 C 本身為那個 base class template 的 template argument。被具現出的 base class template 於是獲得 subclass(C)的型別資訊而可「特化」自己的 data members / member functions,而後者又被 subclass 所繼承,最後 subclass 會獲得為自己特化的功能。型如:

template <typename T>
class CRTPbase { /* ... */ };

class C : public CRTPbase<C> {
  // ...
};

這個手法到了 Java 一樣可以實現:

class CRGbase<T> {  // 目前對 T 沒有任何限制
  // ...
}

class C extends CRGbase<C> {
  // ...
}

但我們對於 CRGbase 的型別參數(type parameter)T 實際上並非全無限制:我們希望 CRGbase 只用於 CRG(Curiously Recurring Generics)手法,也就是像 class T extends CRGbase<T> 這樣的形式,其中 T 是任意型別 ─ 哈,那個式子正好是可提供給 CRGbase 的型別引數形式!所以 CRGbase 之型別參數的限制只需照著那形式填進去就行了:

class CRGbase<T extends CRGbase<T>> {  // 我們想要的結果
  // ...
}

C++ 的話,現行可採用《Static C++ Template Argument Constraints Checking》的作法,未來 C++0x 可用 standard library 的 is_base_of type-trait class 搭配 static_assert,即可完成類似檢驗。

--
就算這式子讀得通了,也只是冰山一角,Java Generics 還有更多更不直覺的東西 XD。

Blogger yen38/29/2006 1:10 pm 說:

CRTP感覺上這是一個頗有趣的技法

 
Anonymous 遊客8/30/2006 6:20 pm 說:

以前 Bruce Eckel 發表了一篇文章
Self-bounding generics

裡面的討論有說到 CRGbase 中的 T 不一定
等於繼承它的型別 , 因為有這種情況:

class A extends HasF<A>
{
}

class B extends HasF<A>
{
}

 
Blogger Josh Ko8/31/2006 1:59 am 說:

這樣感覺好像作弊 :P。B 所繼承的 HasF<A> 之所以能通過,是因為前面已經有滿足其型別變數限制的宣告了。

不過這麼說來,單純用 type bound 想強制用戶使用 CRG 形式,行不通呢。

 

<< 回到主頁