【プログラミングしてみよう16】Javaのジェネリクス(Generics)で型安全なコードを書く

Java

前回はテキストファイルの読み書きを学び、データを永続的に保存できるようになりました。 今回は「ジェネリクス(Generics)」について解説します。 ArrayListを使ったときに登場した <String><Integer> という書き方がジェネリクスです。その仕組みを理解することで、より安全で再利用しやすいコードが書けるようになります。

ジェネリクスの基本

これまで ArrayList<String>ArrayList<Integer> のように、<> の中にデータ型を指定してきました。この <> の仕組みがジェネリクスです。

ジェネリクスを使わない場合、たとえば「何でも入れられる箱クラス」を作ろうとすると、取り出すたびにキャスト(型変換)が必要になり、型を間違えると実行時にエラーが起きてしまいます。

// ジェネリクスなしの場合
Object box = "こんにちは";
String value = (String) box; // キャストが必要

ジェネリクスを使うと、クラスを定義するときに型をあとから決めることができ、コンパイル時に型の間違いを検出できるようになります。

クラス名<型パラメータ>

慣習として、型パラメータには T(Type)、E(Element)、K(Key)、V(Value)などのアルファベット1文字がよく使われます。

サンプルコード(ジェネリクスを使ったクラス)

ファイル作成

「Java06」フォルダに Box.javaGenericsTest.java という名前でファイルを作成します

コード入力

Box.java に以下のコードを入力します。

public class Box<T> {
    private T content;

    void put(T item) {
        content = item;
        System.out.println("箱に入れました:" + item);
    }

    T get() {
        return content;
    }
}

GenericsTest.java に以下のコードを入力します。

public class GenericsTest {
    public static void main(String[] args) {
        Box<String> stringBox = new Box<>();
        stringBox.put("こんにちは");
        String message = stringBox.get();
        System.out.println("取り出した値:" + message);

        Box<Integer> intBox = new Box<>();
        intBox.put(100);
        int number = intBox.get();
        System.out.println("取り出した値:" + number);
    }
}

コンパイル

javac Box.java GenericsTest.java

実行

java GenericsTest

実行結果は次のようになります。

箱に入れました:こんにちは
取り出した値:こんにちは
箱に入れました:100
取り出した値:100

コードの解説

Box<T>T は「型パラメータ」と呼ばれ、インスタンスを作る際に実際の型を指定することで、その型に合わせたクラスとして動作します。Box<String> とすれば T がすべて String に、Box<Integer> とすれば T がすべて Integer に置き換わって動作するイメージです。

private T content; はフィールドで、put(T item) で値を受け取って保存し、get() で取り出します。Box<String> のインスタンスに対して stringBox.get() を呼ぶと、キャストなしで String 型の値が返ってきます。もし誤って数値を入れようとすると、コンパイル時にエラーが出るため、実行前に型の間違いに気づくことができます。

これがジェネリクスの大きなメリットです。ジェネリクスなしで同じことをしようとすると、Object 型を使う必要があり、取り出すたびに (String) のようなキャストが必要になります。キャストを間違えると実行時に ClassCastException というエラーが発生しますが、ジェネリクスを使えばこうしたエラーをコンパイル時に防ぐことができます。

ArrayListがまさにこの仕組みを使っています。ArrayList<String> とすることで、String 以外のデータを入れようとするとコンパイルエラーになり、取り出すときもキャスト不要で String 型として使えます。これまで何気なく使っていた <> の意味が、これで理解できたと思います。

メソッドにもジェネリクスを使うことができます。次のように書くと、どんな型の値でも受け取って表示するメソッドを1つで定義できます。

public static <T> void printValue(T value) {
    System.out.println("値:" + value);
}

呼び出す際は printValue("テスト")printValue(42) のように、通常のメソッドと同じように使えます。

まとめ

ジェネリクスを使うことで、型をあとから決められる柔軟なクラスやメソッドを定義できます。キャストが不要になり、型の間違いをコンパイル時に検出できるため、より安全で読みやすいコードが書けるようになります。これまで ArrayList<String><> を深く考えずに使っていた方も、今回でその意味が理解できたはずです。ジェネリクスはJavaの標準ライブラリでも広く使われているため、読み解く力がつくと既存のコードが一層理解しやすくなります。

2026/06/25(木) ぱんまる

コメント

タイトルとURLをコピーしました