🙄

再帰的な型パラメータと自己型について

に公開

はじめに

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<T>を実装している型に限定されます:

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<T>> 比較可能性を型レベルで制約できる
Javaに自己型はない その代替手段として疑似自己型イデオムが使われる
メソッドチェーンとの相性が良い ビルダーパターンなどで型安全な設計が可能

おわりに

引き続き、Effective Javaの勉強をしていきたいです。

Discussion