はじめに
Effective Javaを読んでいて、出てきた「再帰的な型パラメータ」と「自己型」がわからなかったので、調べてまとめました。
再帰的な型パラメータとは?
再帰的な型パラメータとは、「型パラメータ自身を、制約(上限)として再び参照する」ような書き方です。
これにより、「TはT自身と比較可能な型でなければならない」などの制約を表現できます。
代表例:Comparable
Javaの標準ライブラリにある Comparable インタフェースがその代表です。
public interface Comparable<T> {
int compareTo(T o);
}
多くのクラスでは、以下のように自分自身を型引数に指定して使います。
public class String implements Comparable<String> {
public int compareTo(String o) {
// String同士の比較
}
}
より厳密な宣言
以下のように宣言すると、TはComparableを実装している型に限定されます:
public interface Comparable<T extends Comparable<T>> {
int compareTo(T o);
}
これが「再帰的な型パラメータ」の形です。
なぜ再帰的な型パラメータが必要?
たとえば、コレクション内の最大値を求めるメソッド:
public static <T extends Comparable<T>> T max(Collection<T> c) { ... }
このように宣言することで、「TがT自身と比較できること」がコンパイル時に保証されます。
比較対象が不明確な例
以下のように Comparable<?> にしてしまうと:
public static <T extends Comparable<?>> T max(Collection<T> c) { ... }
比較対象が T である保証がなくなり、型安全ではなくなります。
List<Comparable<Object>> list = ...;
max(list); // コンパイルは通るが、比較の意味は曖昧
自己型(Self Type)とは?
本来の「自己型(Self Type)」とは、「このクラス自身の型」を意味する型システム上の概念です。
Scalaなどの言語ではサポートされていますが、Javaには明示的な自己型は存在しません。
Java における自己型の課題
Javaでメソッドチェーンを使って次のように書きたいとします:
new SubBuilder().foo().bar();
通常の書き方では、foo() の戻り値が BaseBuilder になってしまい、その後の bar() を呼べません。
例(うまくいかない)
class BaseBuilder {
public BaseBuilder foo() {
return this;
}
}
class SubBuilder extends BaseBuilder {
public SubBuilder bar() {
return this;
}
}
new SubBuilder().foo().bar(); // エラー
疑似自己型イデオムで解決!
Java では「再帰的な型パラメータ」を使って疑似的に自己型を実現できます。
基底クラスの工夫
class BaseBuilder<T extends BaseBuilder<T>> {
public T foo() {
return self();
}
protected T self() {
return (T) this;
}
}
サブクラスの実装
class SubBuilder extends BaseBuilder<SubBuilder> {
public SubBuilder bar() {
return this;
}
}
これで、以下のようなコードが型安全に書けるようになります。
new SubBuilder().foo().bar(); // OK
疑似自己型イデオムの構成
| 要素 | 内容 |
|---|---|
<T extends BaseBuilder<T>> |
再帰的な型パラメータで自己型を表現 |
foo() の戻り値を T に |
サブクラスで型が適切に適用される |
self() メソッドで型キャストを統一 |
安全で柔軟なキャスト処理 |
⚠ 注意点
-
self()内のキャストは安全設計が前提(設計ミスがあるとClassCastExceptionになる) - ジェネリクスの構文が複雑になり、理解が難しくなることも
まとめ
| 項目 | ポイント |
|---|---|
| 再帰的な型パラメータ | 型の安全性・表現力を高めるための仕組み |
| Comparable<T extends Comparable> | 比較可能性を型レベルで制約できる |
| Javaに自己型はない | その代替手段として疑似自己型イデオムが使われる |
| メソッドチェーンとの相性が良い | ビルダーパターンなどで型安全な設計が可能 |
おわりに
引き続き、Effective Javaの勉強をしていきたいです。