C#などでは「ジェネリック」と表記されていますが、Javaの日本語ドキュメントではJava SE6まで「総称」、Java SE7では「ジェネリクス」となっています。
ジェネリックのおもな用途はコレクションです。
class クラス名<E> { E フィールド名; }
このときクラス名は原型 (raw type) と呼ばれます。型変数 (type variable)「E」は、型パラメータ (type parameter) と呼ばれ、オブジェクトの生成時には具体的な型で置き換えます。
型変数の名前に特別な意味はありませんが、一般的には次の名前が使用されます。
型パラメータは、カンマ区切りで複数記述できます。
class クラス名<E1, E2, ..., En> {}
たとえば、
class MyClass<E> { E field1; }
と定義されたジェネリック型のオブジェクトを生成するには、
MyClass<Integer> myClass = new MyClass<Integer>();
のように記述します。
型変数<E>
に指定できるのは参照型のみで、基本型は使えません。よって、たとえばList<int>
とは書けず、ラッパークラスを使用してList<Integer>
とする必要があります。もし基本型を指定するならば「Syntax error on token "int", Dimensions expected after this token」のようなエラーとなります。
ジェネリック型であることから、その型変数を型とするフィールドfield1には、生成時に指定した型と同一の型を設定できます。
MyClass<Integer> myClass1 = new MyClass<Integer>(); myClass1.field1 = 10; // 数値を設定 MyClass<String> myClass2 = new MyClass<String>(); myClass2.field1 = "abc"; // 文字列を設定
なお、生成時に型を指定しないと「MyClass is a raw type. References to generic type MyClass<E> should be parameterized」のように警告されます。java - What is a raw type and why shouldn't we use it? - Stack Overflow
Java SE 7以降では、コンストラクタの型変数を省略できます。つまり、
MyClass<Integer> myClass = new MyClass<Integer>();
の記述は、
MyClass<Integer> myClass = new MyClass<>();
のように簡略化できます。ちなみにこのときの「<>
」を、ダイヤモンド (diamond) と呼称します。
インターフェイスが、
interface MyInterface<E> { void Method(E value); }
のようにジェネリック型として定義されているとき、これをクラスで実装するには
class Abc implements MyInterface<Integer> { public void Method(Integer value){} }
のようにします。一方でジェネリックなクラスとして実装するには、
class Abc<E> implements MyInterface<E> { public void Method(E value){} }
とします。
前項のジェネリック インターフェイスの実装と、考え方は同じです。
通常、型パラメータには任意の型を適用できます。しかし、ときには適用される型を制限したい場合もあるでしょう。そのような場合には、
class SubClass<E extends MyClass> {}
のように記述することで、ジェネリック型SubClassのオブジェクトを生成するとき、その型に指定できるのはクラスMyClassを継承したものに限定されるようになります。なお、このときの型パラメータ「E」を、境界のある型パラメータと呼びます。
多数の型を適用対象にできます。それには、
class SubClass<E extends MyClass1 & MyClass2 & MyClass3> {}
のように、複数の型を「&」で連結して記述します。このとき型の1つがクラスならば、その型は必ず最初に記述しなければなりません。またクラスの継承と同様に、拡張できるクラスは1つのみです。
型引数のワイルドカードである「?」を使用することで、適用される型変数を拡張できます。
たとえば次のように定義されたメソッドには、
void Method(List<Number> list) {}
引数としてList<Number>
しか渡せません。これを、
void Method(List<? extends Number> list) {}
とすることで、NumberもしくはNumberのサブクラスであるListを渡せるようになります。
拡張する範囲によって、境界ワイルドカードは次の3つがあります。
種類 | 適用範囲の変化 | サンプルコード |
---|---|---|
上限境界ワイルドカード | サブクラスに拡張 | List<? extends Integer> |
下限境界ワイルドカード | スーパークラスに拡張 | List<? super Integer> |
非境界ワイルドカード | 任意の型に拡張 | List<?> List<? extends Object> と同義 |