Open5

Recursive Generics and Java's Type Erasure

k20kuk20ku

モチベーション

"Effective Java Third Edition" [1]Builderパターン

// Builder pattern for class hierarchies
public abstract class Pizza {
    public enum Topping {
        HAM, MUSHROOM, ONION, PEPPER, SAUSAGE
    }

    final Set<Topping> toppings;

    abstract static class Builder<T extends Builder<T>> {
        EnumSet<Topping> toppings = EnumSet.noneOf(Topping.class);

        public T addTopping(Topping topping) {
            toppings.add(Objects.requireNonNull(topping));
            return self();
        }

        abstract Pizza build();

        // Subclasses must override this method to return "this"
        protected abstract T self();
    }

    Pizza(Builder<?> builder) {
        toppings = builder.toppings.clone(); // See Item 50
    }
}
脚注
  1. Bloch, J. (2017). Effective java. Addison-Wesley Professional. ↩︎

k20kuk20ku
abstract static class Builder<T extends Builder<T>> {...}

この再帰ジェネリクスが気になる.

k20kuk20ku

interfaceしか定義してない.

どうやらjavac側でコンパイラの実装はやってるらしい.

例えば

javax.lang.model.util.Types インターフェースのスケルトン実装: https://github.com/fschopp/java-types

k20kuk20ku

深掘りしすぎて理論と実装が渋滞し始めた感じ

ここまでの流れをざっくり整理すると:

🧵 話題のざっくりマップ

  1. Javaの型システムとErasure

    • 再帰ジェネリクス(Builder<T extends Builder<T>>
    • 実行時にはただの Builder になる理由(型消去)
  2. Kotlinとの違い

    • Builder パターン = 命名引数 + デフォルト引数
    • reified 型パラメータで型情報を保持できる
    inline fun <reified T> inject(): T {
     // リフレクションなしでTのクラスが取れる
        return T::class.java.newInstance()
    }
    inline fun <reified T> createInstance(): T {
       return T::class.java.getDeclaredConstructor().newInstance()
    }
    

    呼び出し時

    val foo: Foo = createInstance<Foo>()
    
  3. 型検査のアルゴリズム

    • 名前ベース vs 構造ベース vs 型推論 vs サブタイプ判定
  4. アノテーションと型引数

    • @Composable の思想と、Javaにおける型アノテーションの可能性
  5. IntelliJの内部

    • PSI型システムとSwingベースのUI設計
  6. Joshua Blochの影響力

    • Enum, Optional, ImmutableList が好きなのも納得