Java

<デバックをコンパイルにやらせる>←これ ジェネリクス#2

ジェネリクスとは??

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

【もし型がなかったら何がおこるか】ジェネリクス#1もし型がなかったら Javaでは変数を用いる時、必ず型(type)を指定します。変数には、その指定した型の情報(数値やインスタンス)し...

ジェネリクスの宣言方法

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は何でも受け付ける

1 2 3 4