ジェネリクスとは??

ジェネリクスが必要な理由などは次の記事がご覧ください

ジェネリクスの宣言方法

APIに準備されたString型や、自分で宣言したHero型クラスなどのクラスやメソッドはそのままでは、<〜>記法は使えません。ArrayListやHashMapにだけ<〜>記法が許されているのは、それらが普通のクラスとしてではなく、ジェネリクスと言われる特別な仕組みを使って定義されています。つまり、自分で宣言するクラスに<〜>を使いたい場合は、<〜>を使って宣言する必要があります。同様にメソッドにも<〜>を使いたい場合は、<〜>を使って宣言する必要があります。
ジェネリクスを使って宣言されたクラスやメソッドは、<〜>記法を利用できる。
<>の中に入るもの

特に定めらていませんが、基本的に1文字で定義します。
がしかし慣例があります。
・E 要素
・K キー
・V 値
・T 型
・N 数値
・S,U,V 2,3,4番目
実際に宣言してみる

public class Box<T>{ private T t; public void add(T t){ this.t = t; } public T get(){ return t; } public <U> void inspect(U u){ System.out.println("T: " + t.getClass().getName()); System.out.println("U: " + t.getClass().getName()); } public static void main(String[] args){ Box<Integer> integerBox; integerBox = new Box<Integer>(); //Box<Integer> integerBox = new Box<Integer>; integerBox.add(new Integer(10)); integerBox.inspect("some text"); } }
ワイルドカードを使う
・上限付きの境界ワイルドカード型
public class Shelf<T> { private T value; public void setValue (T value) { this.value = value; } public T getValue() { return value; } public static void main(String[] args){ Shelf<String> strValue = new Shelf<String>(); Shelf<Integer> intValue = new Shelf<Integer>(); Shelf<? extends Number> list; //listには、NumberかNumberを継承しているクラスがくる list = strValue; //コンパイルエラー //NumberでもないしNumberを継承していない list = intValue; //代入可能 //listとintValueに継承関係があるため //IntegerはNumberを継承している Number value = list.getValue(); //値を取得する際は、Number型として取得される list.setValue("test"); //コンパイルエラー list.setValue(10); //コンパイルエラー //Numberのサブクラスということは保証できるが //?が実際にどのクラスか特定できないため } }
・hogeclass<? extends クラスA>
クラスAとクラスAを継承したクラスをジェネリクスとして使用できる
その変数から値を取り出す(get)際は、上限であるクラスA型のデータ型を返すように動作し意図したとおりに動作しますが、値を設定する(put)際は、putされる値がクラスAのサブクラスということは保証できるが、実際にどのクラスか特定できないため、コンパイルエラーになります。
・下限付きの境界ワイルドカード型
public class Shelf<T> { private T value; public void setValue (T value) { this.value = value; } public T getValue() { return value; } public static void main(String[] args){ Shelf<String> strValue = new Shelf<String>(); Shelf<Number> numValue = new Shelf<Number>(); Shelf<? super Integer> list; //IntegerかIntegerのスーパークラス //(親クラス、基底クラス)//を要素とするクラス list = strValue; //コンパイルエラー //IntegerかIntegerの親クラスではない list = numValue; //代入可能 //exListとnumListに継承関係があるため //NumberはIntergerの親クラス Object value = list.getValue(); //値を取得する際は、Object型として取得される list.setValue("test"); //コンパイルエラー list.setValue(10); //下限であるInteger型の値を設定するように動作する } }
・hogeclass<? super クラスB>
クラスBとクラスBのスーパークラス(親クラス、基底クラス)
その変数に値を設定する(put)際は、下限であるクラスBのデータ型を設定するように動作し意図したとおりに動作しますが、値を取り出す(get)際は、どのデータ型か特定できないためObject型として取り出されます。
注意
クラスで使うジェネリクスとメソッドで使うジェネリクスによって様々な違いがあります。
class AA<E> { // restrictive method E[] method1(E[] arr) { for (E e: arr) System.out.println("m1: " + e.toString()); return arr; } // generic method <T> T[] method2(T[] arr){ for (T e: arr) System.out.println("m2: " + e.toString()); return arr; } public class GenericMethods { public static void main(String[] args) { AA<Number> aa = new AA<Number>(); Object[] a1 = aa.method1(new Integer[] { 1,2 }); /*エラー*/Object[] a2 = aa.method1(new Object[] { 1,2 }); /*エラー*/Object[] a2 = aa.method1(new String[] { "First","Second" }); Object[] a3 = aa.method2(new Object[] { 1,2 }); Object[] a4 = aa.method2(new Object[] { "First","Second" }); for(Object i: a1) System.out.println("In main, a1 element: " + i); for(Object i: a3) System.out.println("In main, a3 element: " + i); for(Object i: a4) System.out.println("In main, a4 element: " + i); } }
・method1はクラスで指定したNumberおよびNumberを継承したクラス型しか受け付けない
・method2は何でも受け付ける