目次
Generics(ジェネリクス・総称型)とは

Genericsとは、クラスやメソッドなどの型を、パラメータとして定義できるようにした機能のことです。
「<>」の中に具体的な型名を付けて利用できます。
Genericsクラスを定義する方法

Genericsクラスを定義するには、「パラメータ化された型」と「型変数」の2つを使います。
パラメータ化された型とは、型パラメータリストをもったクラスのことです。
クラスの宣言時に、クラス名の後ろに「< 型変数 >」という形で、型パラメータを記述します。
記述方法は次の通りです。
class クラス名< 型変数 > { //処理 }
パラメータ化されたクラスを使う場合は、インスタンス化する時に型の指定が必要です。
例えば、String型にする場合は、以下のように記述できます。
GenericsClass gc = new GenericsClass();
Genericsメソッドを定義する方法

ジェネリクスメソッドを定義するには、Genericsクラスで定義した型変数を、引数や戻り値に利用しましょう。
先ほどの、GenericsClassで言えば次のように記述可能です。
public class GenericsClass<String> {
	private String type;
	public GenericsClass(String type) {
		this.type = type;
	}
	public String getType() {
		return type;
	}
}
Genericsの使い方

ここまで、Genericsの定義について紹介しました。
言葉で説明されても難しいと思うので、実際に使い方を見ていきましょう。
例えば、次のように記述できます。
■記述例
public class Main {
    public static void main(String[] args) {
        GenericsClass<String> gcStr = new GenericsClass<String>("PotepanStyle");
        String str = gcStr.getT();
        System.out.println(str);
        GenericsClass<Integer> gcInt = new GenericsClass<Integer>(123456);
        Integer i = gcInt.getT();
        System.out.println(i);
    }
}
class GenericsClass<T>{
    private T t;
    public GenericsClass(T t){
        this.t = t;
    }
    public T getT(){
        return t;
    }
}
GenericsClassクラスのクラス名の後に「<T>」と記述しました。
<T>を指定すると、データ型の指定が「Object型」から「T型(型変数)」へ変更できます。
そして、Mainクラスのmainメソッドで、GenericsClassクラスをインスタンス化するときに、データ型をString型とInteger型で指定しています。
Genericsを使うので、GenericsClassクラスのオブジェクトを呼び出す「getTメソッド」をキャストすることなく、指定した方で値の代入が可能です。
上記のプログラムを実行すると、次の結果を取得できます。
■実行結果 mbp:Desktop potepan$ java Main PotepanStyle 123456
それぞれ、String型とInteger型で使えているのがわかります。
Genericsの「T」とは?

前章のプログラムで「<T>」という表現がありました。
この T とは、Generics型の変数名に使われるネーミングです。
T は Type の略称です。
T以外にも、次のようなネーミングがよく使われます。
- E:Element
- K:Key
- V:Value
- T:Type
- N:Number
- S,U:2、3番目
慣習的に使われるものなので、覚えておくと良いでしょう。
実際の現場やプロジェクトでも、共通のネーミングを使うことで混乱を避けられるはずです!
Genericsのワイルドカード型について

ここでは、Genericsのワイルドカード型について見ていきましょう。
例として、以下のプログラムを用意しました。
■記述例
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class Main {
  public static void main(String[] args) {
    List list = new ArrayList<>();
    list.add("Potepan");
    list.add("Style");
    printStr(list);
  }
  public static void printStr(Collection<String> col) {
    for (String s : col) {
      System.out.print(s);
    }
    System.out.println();
  }
}
■実行結果
mbp:Desktop potepan$ java Main
PotepanStyle
printStrメソッドで、Collection型のコレクションを受け取った上で、その要素を出力しているプログラムです。
Collectionは、Genericsを使って型の指定を行っています。
しかし、使う際にはGenericsの型であるTごとに使う必要があり、少し不便です。
仮に、Integer型で扱いたい場合は、それぞれ別個で扱わないといけません。
そこで、便利なのがワイルドカード型を使った方法です。
ワイルドカードを「?」で表現し、「Collection<?>」します。
そうすることで、任意の型のコレクションとして扱えるのです。
先ほどのプログラムを書き換えて実行してみます。
■記述例
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class Main {
  public static void main(String[] args) {
    List listStr = new ArrayList<>();
    listStr.add("Potepan");
    listStr.add("Style");
    printStr(listStr);
    List listInt = new ArrayList<>();
    listInt.add(12345);
    listInt.add(67890);
    printStr(listInt);
  }
  public static void printStr(Collection<?> col) {
    for (Object o : col) {
      System.out.print(o);
    }
    System.out.println();
  }
}
■実行結果
mbp:Desktop potepan$ java Main
PotepanStyle
1234567890
このように、String型でもInteger型でも使えるようになります!
Genericsでextendsを使う方法

Genericsでは、型パラメータを宣言するときに、「extends」が使えます。
extendsを使うと、データ型として指定できるものを制限可能です。
例えば、<T extends P>と宣言した場合、データ型として指定できる型は、クラスPを継承したデータ型となります。
extendsで指定できるクラスは1つだけです。
複数指定する際は「&」でつなげることでインタフェースを指定できます。
class クラス名<型パラメーター extends クラスorインタフェース & インタフェース & ・・・> { クラス本体 }
では、実際にプログラムを実行してみましょう!
次のように記述できます。
■記述例
public class Main {
    public static void main(String[] args) {
      GenericsClass<Integer> gc = new GenericsClass<Integer>();
      gc.setValue(123456);
      Integer i = gc.getValue();
      System.out.println(i);
    }
}
class GenericsClass<T extends Number> {
  private T t;
  public void setValue (T t) {
    this.t = t;
  }
  public T getValue() {
    return t;
  }
}
上記の例では、TはNumber型を継承したデータ型だけを扱えるようになります。
そのため、Integer型は使えますが、String型は使えません。
String型を指定した場合、コンパイルエラーになります。
上記のプログラムを実行した結果は次の通りです。
■実行結果 mbp:Desktop potepan$ java Main 123456
Integer型として出力されているのがわかります。
このように、扱えるデータ型を制限したい場合に、extendsは便利な機能です!
Genericsでsuperを使う方法

Genericsでsuperを使うことで、extendsとは逆の動作を実現することも可能です。
例えば、次のように記述できます。
■記述例
public class Main {
    public static void main(String[] args) {
      GenericsClass<Number> gc1 = new GenericsClass<Number>(123456);
      GenericsClass<? super Integer> gc2;
      gc2 = gc1;
      Object obj = gc2.getValue();
      System.out.println(obj);
    }
}
class GenericsClass<T> {
  private T t;
  public GenericsClass(T t) {
    this.t = t;
  }
  public T getValue() {
    return t;
  }
}
gs1というNumber型で指定したインスタンスと、<? super Integer>で指定したgs2インスタンスを用意します。
そして、gs1をgs2に代入する際に、Object型の変数「obj」を使いました。
上記のプログラムを実行すると、次のように表示されます。
■実行結果 mbp:Desktop potepan$ java Main 123456
Genericsでメソッドで使う方法

「Genericsメソッドを定義する方法」を紹介したように、メソッドでもGenericsは使用可能です。
使う際は、次のように記述しましょう。
■記述例
public class Main {
    public static void main(String[] args) {
      String str = getT("PotepanStyle");
      System.out.println(str);
      Integer i = getT(123456);
      System.out.println(i);
    }
    private static <T> T getT(T t){
        return t;
    }
}
■実行結果
mbp:Desktop potepan$ java Main
PotepanStyle
123456
Genericsクラスと同じように、String型とInteger型で取得できているのがわかります!
まとめ

JavaのGenericsについて、クラス・メソッドの定義方法や使い方を解説しました。
Genericsは、Integer型やString型といったさまざまなデータ型で、同じ処理を実行したい場合に便利です。
プログラム実行時のエラーが発生する危険がなくなるのも、メリットと言えます。
ぜひこの記事を参考に、Genericsの使い方を覚えてください!
 
               
     
     
     
     
   
       
     
  