頭脳一式

人の記憶なんて曖昧なもの。すべての情報を頭に記憶するなんてナンセンス。困ったらここに来ればいいじゃん?というスタンスで最強のナレッジベースを目指すブログ

【Java】ジェネリクス(総称型)と型推論

ジェネリクス(総称型)とは

ジェネリクスとは、型を指定することによってコレクションが扱える型を限定的にする機能。
例えば、ArrayListを下記のように記述した場合、これはArrayListがString型しか扱えないことを指す。

ArrayList<String> list= new ArrayList<String>();

指定する型のことを型パラメータといい、ダイヤモンド演算子『<>』を用いて記述する。
なお、下記のように型変数を指定しない場合はObject型が渡されたものと推論される。

ArrayList list= new ArrayList();

ジェネリクスのメリット①:意図しない型による実行時エラーを防ぐ

例えば以下のようなコード。
このArrayListは型を指定していないため、どんな型の値でも扱うことができる。

ArrayList list= new ArrayList<>();
list.add("abc");    //String
list.add(123);    //Integer
list.add(true);    //boolean

ジェネリクスを使わない場合、.get()メソッドで値を取得する際、String型が来るとわかっていてもキャストしなければならず、
またString型以外の値が来たときは実行時エラーになってしまう。

ArrayList list= new ArrayList();
list.add("abc");
list.add(123);
list.add(true);

String str1 = list.get(0);//この書き方ではコンパイルエラー。
String str2 = (String) list.get(0);//上のコードは←のようにキャストする必要がある。
String str3 = (String) list.get(1);//中身がint型なので実行するとClassCastExceptionが発生する。 

下記のようにジェネリクスを利用することにより、コーディング段階で誤りに気付ける。

ArrayList<String> list= new ArrayList<String>();
list.add("abc");
list.add(123);    //ここでコンパイルエラーが発生。
list.add(true);    //ここでコンパイルエラーが発生。

ジェネリクスのメリット②:クラスの型を汎用的にできる

public class Generics<T> {
    private T element;

    public void setter(T element) {
        this.element = element;
    }

    public T getter() {
        return element;
    }
}

上記の<T>の部分にStringやIntegerを入れてインスタンス化することにより、
String型のGenericsクラスやInteger型のGenericsクラスを作ることが出来る。

Generics<String> a = new Generics<String>();
a.setter("abc");
System.out.println(a.getter()); //abcが返却される。

Generics<Integer> b = new Generics<Integer>();
b.setter(123);
System.out.println(b.getter());    //123が返却される。

用語おさらい

用語 説明
<> ダイヤモンド演算子
<T> 型変数
型パラメータ Tの部分に代入するStringやIntegerのこと。

型推論とは

型推論とは、インスタンス生成時に、変数の型を決定する際、変数の宣言時と同じ型を使う機能のこと。
これにより今まで宣言時とインスタンス生成時の2箇所で型を記述していたのが1回で済むようになる。

ArrayList<String> list= new ArrayList<String>();  //従来の書き方
ArrayList<String> list2= new ArrayList<>();   //これが型推論