ジェネリクス

Jdk1.4までHashMap,Listのキーや値がオブジェクト型でオブジェクト型であればどのようなオブジェクトでもコンテンナに格納できました。

ジェネリクス(Generics)とは、今ままでオブジェクト型で指定していたオブジェクトの型(クラス)を明示することで,
タイプセーフ(型の安全性)をコンパイル時に保証するためにJava5より導入された構文です。

ジェネリクス構文ができる前に起きたバグ達

Java5が誕生する前のjdk1.4およびJ2EE1.4が全盛期の時代(Javaの基本知識-解答-8.Javaの歴史 )には、
Web3階層におけるクラス設計やフレームワークが確立していない時期でした。
その当時もっともよく利用されていたクラスといえば、HashMap及びArrayListです。

HashMapは連想配列として利用します。キー(主にStringの文字列)に対して、値のオブジェクトを格納します。

Jdk1.4サンプル
    Map map = new HashMap();
    map.put("first_name", "太郎");
    map.put("last_name", "山田");
    List checkBoxList = new ArrayList();
    checkBoxList.add("01");
    checkBoxList.add("02");
    checkBoxList.add("03");
    map.put( checkBoxList );
    //オブジェクト型からStringへ型キャスト
    String  first_name = (String)map.get("first_name");
				

※.Java5以上では警告になりますが、jdk1.4系は警告にならずコンパイルできます。

Jdk1.4では、オブジェクト(java.lang.Object)を継承したクラス即ちプリミティブ型を除く全てのクラスが警告無しで値に設定できました。
しかも、HashMapの値(Value)に設定する型が統一されていれば問題ないのですが、
2004年頃は、2017現在に比べてJavaに対して厳密なポリシーを持って表現できるエンジニアが少なかった時代だったので、
上記例のようなString型以外のArrayList等を値に全て引き渡すような事が平気で行われていました。

この結果は明白ですよね。記述した本人でさえ、機能拡張などの改修時にトラップになってバグを作っていました。
これは、Jdk1.4のJavaAPI上Objectをキーと値に設定できる仕様なので、コーディング規約のルールベースで自衛するしかありません。

この要望が多く集まったためにJava5からジェネリックスが導入され、格納するオブジェクトをHashMap,ArrayListのインスタンス生成時に
型を<オブジェクト型>で明示する事ができるようになりました。

型を明示した事により、インスタンスを利用する際に異なる型を設定するとエラーになります。

Java5以上サンプル
    Map<String,String> map = new HashMap<String,String>();
    map.put("first_name", "太郎");
    map.put("last_name", "山田");

    List<String> list = new ArrayList<String>();
    list.add( "山田 太郎");
    list.add( "東京都hoge");

    //型を明示しているので型キャストが不要
    String first_name = map.get("first_name");
				

Java8となった今では当たり前になっている<型>で定義するジェネリックス構文です。

これにより、HashMap及びListに格納する型が制限されるようにった上、HashMapやListから取り出す際の型キャストが不要になりました。

ジェネリクス構文で型を指定するのは、開発現場では当たり前のお作法です。

ジェネリクスとは

ジェェネリックスは、クラス、インタフェース、メソッドを定義するときに型(クラスとインタフェース)をパラメータにすることができます。

メソッド宣言で使用されるよりよく知られている仮型パラメータと同様に、型パラメータは、異なるコードで同じコードを再利用する方法を提供します。

違いは、仮型パラメータへの入力は値であり、型パラメータへの入力は型であることです。

単語 英語 説明
パラメータ化された型 Parameterized type List <String> String持つList型を定義する
StringがパラメータListが型
原型 raw type List 総称型から仮型パラメータを除いた型
実型パラメータ Actual type parameter String 実引数がString型を意味する。
ジェネリクス型又は総称型 Generic type List <E> 型変数EをもつList型を定義する
仮型パラメータ Formal type parameter E 仮型のパラメータ名E(Elements)を意味する
境界ワイルドパラメータ bounded type parameter < E extends Number> Number型を継承した仮型パラメータを意味する。
非境界ワイルドカード型 Unbounded wildcard type List <?> 型引数が不定であるList型を定義する
非境界ワイルドカード型 Unbounded wildcard type List <?> 型引数が不定であるList型を定義する
境界ワイルドカード型 Bounded wildcard type List <? extends Number> 型引数がNumber型を継承した仮型パラメータを意味する。
恐らくエッセンシャルJavaからの引用

Joshua Blochの「Effective Java プログラミング言語ガイド」が日本語訳であります。

ジェネリクス構文

ジェネリクス構文-クラス定義
public class GenericsSample<T1, T2,...,Tn>{

//処理

}
ジェネリクス構文-フィールド
privte T1;
ジェネリクス構文-デフォルトコンストラクタ
public GenericsSample( T1 obj1, T2 obj2 ...m, Tn objN ) {

this.obj1 = obj1;

this.objN = objN;

}
ジェネリクス構文-インスタンス生成時
GenericsSample<String,Integer, ..., String> gs = GenericsSample( "hoge", new Integer(1) , ... , "hogeN" );

T1,T2,T2,Eなどは、実はa,b,cなど変数のように仮型パラメータを決める事ができます。
しかしながら、仮型パラメータのを英小文字で指定すると引数パラメータ名 obj1,obj2と区別がつき難くなります
このことから仮型パラメータを利用する場合には、英太文字必要に応じて添え字の1~Nで構成します。

ジェネリクス構文の仮型パラメータの慣習

記号 英語意味 説明
T type クラス名 <T> クラス名は仮型パラメータTを格納するコンテナクラスである
E elements List <E> コレクション型の格納するオブジェクトの型を意味します。
K key HashMap<K,V> HashMapのようにオブジェクトにキー(Key)に対する値(Value)を格納する場合のキーを意味します。
V value HashMap<K , V> HashMapのようにオブジェクトにキー(Key)に対する値(Value)を格納する場合の値を意味します。
N Number クラス名 <N> クラス名の仮型パラメータがNumber型である仮型パラメータ名Nを意味します。

ダイヤモンド

ダイヤモンドとは、<>だけで変数にジェネリクス型宣言を行い、インスタンスの生成に仮型パラメータの引数を宣言しない方法です。

Java5以上サンプル
    Map<String,String> map = new HashMap<>();
    map.put("first_name", "太郎");
    map.put("last_name", "山田");

    List<String> list = new ArrayList<>();
    list.add( "山田 太郎");
    list.add( "東京都hoge");
				

必要最低限知るべきくこと

ジェネリクス型が活用されるのは、オープンソースのフレームワークを独自で作るなどで利用されるでしょう。
ビジネスシステムでは、上記HashMapとListの事を抑えておけば良いだけです。

2017/03/12時点ジェネリクス型の深堀は、全体のコンテンツの進捗と要望を考慮して追加します。