Javaジェネリクス(Generics)

Genericsとは?

ジェネリクス(Generics)はJava 5で導入されたもので、データ型を一般化する(Generalize) という意味である。
ジェネリクスは、クラスまたはメソッドで使用するデータ型をあらかじめ指定する方法である。
ジェネリクスを使用すると、データ型の不一致問題をコンパイル過程で防ぐことができる。
また、ジェネリクスを使用すると型キャストを行う必要がなくなるため、プログラムの性能が向上する。

BadGenericsSample.java

package com.devkuma.basic.generics;

import java.util.ArrayList;
import java.util.List;

public class BadGenericsSample {
    public static void main(String[] args) {
        List arrayList = new ArrayList();
        arrayList.add("test");

        String test = arrayList.get(0); // error
    }
}

上の例のように、ジェネリクスでデータ型を指定せずにデータを保存すると、保存されるデータはObject型として保存される。 そのため、取り出すときには必ず型変換を行う必要がある。

GoodGenericsSample.java

import java.util.ArrayList;
import java.util.List;

public class GoodGenericsSample {
    public static void main(String[] args) {
        List<String> arrayList = new ArrayList();
        arrayList.add("test");

        String test = arrayList.get(0); // ok
    }
}

ジェネリクスでデータ型をあらかじめ指定すると、保存されるデータが指定された型として保存されるため、データを取り出すときに型変換を行わなくてもよい。

ジェネリクスの使い方

ジェネリクスは以下のように宣言する。

public class クラス名<T> 
public interface インターフェース名<E>

<>括弧の中に入れる文字に決まった規則はないが、一般的には大文字のアルファベットで表す。

クラスでジェネリクスを使用

以下はクラスでジェネリクスを使用した例である。

package com.devkuma.basic.generics.ex2;

public class GenericsSampleClass<T> {

    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}

上のように作成したクラスは、インスタンスを生成して使用するときにデータ型を指定して使用する。

package com.devkuma.basic.generics.ex2;

public class GenericsSample {

    public static void main(String[] args) {
        GenericsSampleClass<String> classSample = new GenericsSampleClass();
        classSample.setT("SetItem");
        String getItem = classSample.getT();
    }
}

インターフェースでジェネリクスを使用

以下はインターフェースでジェネリクスを使用した例である。

package com.devkuma.basic.generics.ex3;

public interface GenericInterfaceSample<T> {
    T testMethod(T t);
}

上のように作成されたインターフェースは、実装クラスでデータ型を指定できる。

package com.devkuma.basic.generics.ex3;

public class GenericInterfaceSampleImpl implements GenericInterfaceSample<String> {

    @Override
    public String testMethod(String t) {
        return null;
    }
}

ジェネリクスの範囲指定

ジェネリクスで指定するデータ型の範囲も、継承を利用すると制限できる。

package com.devkuma.basic.generics.ex4;

public class GenericClassSample<T extends Number>{
    private T t;

    public T getT() {
        return t;
    }

    public void setT(T t) {
        this.t = t;
    }
}

上のように作成されたクラスは、ジェネリクスでデータ型を指定するとき、Numberクラスを継承するクラスだけを指定できる。

package com.devkuma.basic.generics.ex4;

public class GenericSample {
    public static void main(String[] args) {
        GenericClassSample<String> classSample = new GenericClassSample(); // error
        classSample.setT("SetItem");
        String getItem = classSample.getT();
    }
}

上のようにジェネリクスでデータ型をStringとして指定しているが、StringNumberクラスの下位クラスではないため、コンパイルエラーが発生する。