ジェネリクス(Generics)とは、Javaで変数やクラスの「型をあとから自由に指定できる仕組み」です。以下のようなコードを見て、<>って何?と思ってこのページにたどり着いた方も多いはずですが
ArrayList<String> list = new ArrayList<>();
この「<String>
」の部分がジェネリクスです。
ジェネリクスとは?
ジェネリクス(Generics)とは、Javaで使われる「型を指定できる仕組み」のことです。

…でも、これだけだとまだイメージが湧きませんよね。
もう少しわかりやすく言うと、
- 「この箱には、文字列だけを入れたい」
- 「あの箱には、数字だけを入れたい」
のように、「扱えるデータの種類をあらかじめ指定することができる」のがジェネリクスの基本的な考え方です。
ジェネリクスが登場した理由とメリット
昔のJavaにはジェネリクスがありませんでした。そのため…
- 箱に色々な種類のデータが混ざってしまう
- 取り出すときに「これって何型だったかな?」とキャスト(型変換)が必要で面倒だった
そこで登場したのがジェネリクス。メリットは3つあります。
メリット | 意味 | 効果 |
---|---|---|
型安全 | 決めた型しか入れないようにする | 間違った型を入れられない |
キャスト不要 | 型が決まっているので変換不要 | コードが短くなりスッキリ |
読みやすい | 一目で型がわかる | 他の人がコードを見た時もわかりやすい |
まずはイメージをつかむ
ジェネリクス無しの世界😢
[りんご, 100, バナナ, 200, メロン…]
- 何が入っているか不明
- 取り出してから「これは何?」と確認が必要
ジェネリクス有りの世界✨
箱A<String>:[りんご, バナナ, メロン] 箱B<Integer>:[100, 200, 300]
- 最初から型が決まっていて、混ざることがない
- 安全で取り出しやすい!
そのため、ジェネリクスは、ArrayList
や HashMap
などのコレクション系クラスで最もよく使われます。
コレクションは「複数の値をまとめて持つ」ので、何の型を入れるか決めることにおおきなメリットがあるためです。もちろんコレクション以外でも利用できるシーンはありますが、基本はコレクション系のクラスでよく使える!っていう風に理解しておくとよいでしょう。
ジェネリクスの基本構文
ジェネリクスは次のように書きます。
クラス名<型名> 変数名 = new クラス名<型名>();
クラス名
:ArrayListやHashMapなど型名
:StringやIntegerなど扱う型
例えば、「文字列だけを入れる箱」を作るには…
ArrayList<String> fruits = new ArrayList<String>();
これが基本形です。(最近のJavaなら右辺の型は省略可能!)
ArrayList<String> fruits = new ArrayList<>();
ジェネリクスのよくある使い方
ジェネリクスを利用すると、リストから要素を取り出す際に明示的なキャストが不要です。
List<Integer> intList = new ArrayList<>(); intList.add(1); intList.add(2); for (Integer num : intList) { System.out.println(num); }
この例では、intList
に格納される値は常に Integer
型であることが保証されているため、ループ内でキャストする必要がありません。また、誤った型の要素がリストに含まれることを防ぐことができます。
もし、ジェネリクスを使わずに List
を定義すると、以下のように明示的なキャストが必要になります。
List list = new ArrayList(); list.add("Hello"); String str = (String) list.get(0); // キャストが必要
このようなコードでは、リストに異なる型の値が混在する可能性があり、実行時に ClassCastException
が発生するリスクがあります。ジェネリクスを使用することで、上記の問題を解決できます。以下のように書き換えることで、型安全性が向上します。
List<String> list = new ArrayList<>(); list.add("Hello"); String str = list.get(0); // キャスト不要
この場合、リストには String
型の値のみを格納できるため、キャストが不要になります。また、型が保証されるため、誤った型を扱うことによるエラーを未然に防ぐことができます。
【応用編】自分でジェネリクスクラスを作る方法
ジェネリクスはコレクションだけでなく、独自クラスにも適用できます。例えば、以下のような汎用的なクラスを定義する例を見てみましょう。
class Box<T> { private T data; void set(T data) { this.data = data; } T get() { return data; } } Box<String> box = new Box<>(); box.set("Hello"); System.out.println(box.get()); //"Hello"
このコードは「Box
というクラスの中で、あとから使う型(T)を自由に指定できるようにしている」という仕組みです。※ T
というのは、仮の型名(型のプレースホルダー)です。
Box<T>
:これは「このBoxはT型のデータを扱います」という意味。private T data;
:データを入れる変数data
は、T型ですよ〜 という意味。set(T data)
:引数として T型のデータ を受け取って、保存。get()
:保存された T型のデータ を返す。
つまり「このクラスはTという型を扱う、型付きの箱(Box)」ということ。Box<String>
と書いた時点で「T = String」と明確に決まるので、String型のメソッドが自由に使えるようにもなります。
Box<String> box = new Box<>(); box.set("こんにちは"); String value = box.get(); System.out.println(value.length()); // 出力: 5 System.out.println(value.toUpperCase()); // 出力: コンニチハ System.out.println(value.contains("にち")); // 出力: true

ジェネリクスを使用することで、型の安全性を向上させ、明示的なキャストを減らすことができます。これにより、コードが簡潔になり、バグの発生を防ぐことができます。特にコレクションを扱う際は、ジェネリクスを積極的に活用しましょう。