読者です 読者をやめる 読者になる 読者になる

Jの衝動書き日記

さらりーまんSEの日記でございます。

Effective Javaの読書メモ 第5章 ジェネリックス

 今回はジェネリックスに関するメモ。java5から登場した<>のことである。最初は意味不明だったけど、少しはわかった……気がする。

そもそもジェネリックスって何なのか?

  • 許されるオブジェクト型が何であるかをコンパイラに知らせるための機構
    • 原型(List<E>ではなく、Listとして使う)の場合、それは判断できない
      • 要素に入るのがIntergerなのかStringなのかわからないとチェックできない
      • 原型を使うとジェネリックスの安全性と表現力のすべてを失うことになる
  • コンパイラジェネリックスの定義に従いキャストを実施する
    • ソース側ではキャスト不要
    • コンパイラが隠れたキャストを挿入し、キャストが失敗しないことを保証する
  • ジェネリックス型
    • 型パラメータを宣言に持つクラスやインタフェース
  • 実行時には消える
    • instanceof Set<E> ではなく instanceof Set

ジェネリックスの指定

  • パラメータ型は不変である
    • List<Number>の要素に指定できるのはNumberのみ。Integerでは駄目
    • List<E>でEをNumberとして指定した場合、そのクラスで登場するEはすべてNumberでないといけない
  • 境界ワイルドカード
    • ? extends E
      • Eの子クラスならOKという指定:相手が生産者(プロデューサー)
    • ? super E
      • Eの親クラスならOKという指定 :相手が消費者(コンシューマ)
    • 生産者か消費者を表す入力パラメータに対してワイルドカード型を使用する
    • 戻り値としてはワイルドカード型を使用しない
  • 非境界ワイルドカード
    • 実際の型パラメータが何であるかわからなかったり、気にしたりしない場合使用する
    • ?で指定 List<?>:何からの型のリスト等
    • ただし、Collection<?>にはnull以外要素を入れることはできないし、取り出す型の仮定もできない
    • そうしたい場合はジェネリックスか境界ワイルドカード型を使う

配列とリストの違い

  • 配列:共変(covariant)
    • Sub extends Superならば Sub は Superの子クラス
      • Sub[]の要素にSuperのものが入れられる
    • コンパイル時の型安全性は提供しない
    • 実行時の型安全性は提供する
      • ArrayStoreExceptionやClassCastExcepion
    • 具象化不可能型の配列は作成できない:Eの配列等
  • リスト:不変(invariant)
    • Sub extends SuperでもList<Sub>はList<Super>の子クラスではない
      • List<Sub>の要素にSuperは取れない
    • コンパイル時の型安全性を提供する
    • 実行時の型安全性は提供しない
    • 具象化不可能型:実行時の表現がコンパイル時の表現よりも情報が少ない

ジェネリックメソッドのポイント

public static <K,V> HashMap<K,V> newHashMap() {
  return new HashMap<K,V>();
}

Map<String, List<String>> listMap = newHashMap();
<K,V>が型パラメータリスト
K:String,V:List<String>コンパイラ型推論してくれる
  • 型パラメータがその型パラメータ自身が関係する何らかの式で制限することも可能→再帰型境界
    • public static <T extends Comparable<T>> T max(List<T> list)

参考文献

Effective Java 第2版 (The Java Series)

Effective Java 第2版 (The Java Series)