ジェネリクスとは?
状態:-
閲覧数:2,769
投稿日:2013-06-23
更新日:2018-05-29
表記
英語
・Generics
「<>」記号で囲まれたデータ型名を付けることで、汎用的なクラスやメソッドを作る機能
「<>」記号で囲まれたデータ型名をクラスやメソッドに付けることで、Integer型やString型などの様々な型に対応する汎用的なクラスやメソッドを作る機能のこと
・ソウショウガタ
・クラス定義内で用いられる型を変数化することで、インスタンス作成時まで「具体的な型を抽象的に表現」し、インスタンス生成時に「具体的な型を指定」
※「型」を変数にした「型変数」を利用
・コンパイル時に型を保障しながら汎用的なコレクションクラス(動的配列や辞書)を作成することが可能
用途
使用する型を限定するために利用
・キャストすることなく、指定した型に値を代入することができる
ジェネリクスを使用しないと?
データ型の不一致で実行時にエラーが発生する場合がある
・ジェネリックスとは?なぜジェネリックスなのか
ジェネリクスの歴史を振り返る
Java5.0未満
2004年にJavaバージョンが5.0となるまで、Javaにジェネリクス機能はなかった
・キャストに失敗した場合、原因箇所を特定するのはとても骨の折れる作業だった
・要素を Collection から取り出すときは、要素を「コレクションに格納されている要素の型」にキャストする必要があった
Collection系の問題
1. 要素取り出す際にはキャストが必要
2. 意図しない型の要素も追加できてしまう
※コンパイラはキャストがコレクションの型と同じであるかどうかをチェックしないため、実行時にキャストが失敗する恐れがあった
List list = new ArrayList();
list.add("a");
String str = (String) list.get(0);
list.add("a");
String str = (String) list.get(0);
Java5 / Java6
ジェネリクス導入
・目的の型へのキャストが省略可能
・間違った型で扱ってしまっても、コンパイル時点でエラーとなるので間違いが非常に発見しやすい
・ListやMap等のコレクションを返すメソッドの場合、何の型を扱っているかが明白
メリット
・ジェネリクスを使用すると、コレクションの型をコンパイラに通知できるため、型のチェックを行うことができる
・コンパイラがコレクションの要素の型を認識すると、そのコレクションを矛盾なく使用していることをチェックでき、コレクションからの値の適切なキャストを挿入できる
List<String> list = new ArrayList<String>();
Java7
ジェネリックな変数にnewでインスタンスを生成して代入するというケースで、冗長表記を簡素化出来る
・後のStringを省略可能
List<String> list = new ArrayList<>();
型変数にまつわる文法上の記述
混乱しやすい
型変数にまつわる記述はだいたい<>で囲われている。一見して同じように見えるが<>は場所によって数種類に分類される
- | 種類 | 用語 | 例 | 宣言 | バインド | ? | 境界 | & |
---|---|---|---|---|---|---|---|---|
1 | 型変数の宣言 | _ | _ | ◯ | × | △*1 | ◯ | ◯ |
2 | 型変数へのバインド | _ | new Hoge<String>(); | × | ◯ | × | × | × |
3 | 変数の型の宣言 | ジェネリック型(generic type) | List<T> | × | ◯ | ◯ | ◯ | × |
3 | 変数の型の宣言 | パラメータ化された型(parameterized type) | List<String> | × | ◯ | ◯ | ◯ | × |
4 | 型変数での変数宣言 | 型変数の宣言 | ジェネリック型(generic type) | × | × | × | × | × |
<T> <E>
プログラムする際、具体的なクラス名に置き換えて使用する
<E> の場合は要素(Element)の型を指定するので「E」
<K,V> の場合はキー(Key)と値(Value)の型を指定するので「K」「V」
・javaのジェネリックスでTとEの違いが良く分かりません<K,V> の場合はキー(Key)と値(Value)の型を指定するので「K」「V」
T for type, E for Element, V for value and K for key
・Difference between List, List<?>, List<T>, List<E>, and List<Object>1.型変数の宣言
型変数の宣言
・クラス定義する際、新たな型変数の宣言が可能
・「<T>」を記述することで、データ型の指定がT型(型変数)に変更される
public class Hoge<T> {}
・Hoge<T>のTは、new Hoge<String>されるとStringとなり、Hogeクラスの中でTという型変数が使われるときはString型として扱われる・Hoge<T>のTは「仮型引数」と呼ばれる
※型引数は変数名の命名規約と同じだが、慣例としてアルファベットの大文字1字で表現することが多い。2文字以上にする場合は全て大文字にする。クラス名と紛らわしくなるのでキャメルケースにはしない
複数の型引数
・複数の型引数を持たせたい場合はカンマで区切る
public class Piyo<T1, T2, T3> {}
型変数の境界
・型変数の境界も指定できる
public class A {}
public class B extends A {}
public class C extends B {}
public class Foo1<T extends B> {} // BやCやその派生
public class B extends A {}
public class C extends B {}
public class Foo1<T extends B> {} // BやCやその派生
「境界」とは?
・型引数の型が継承階層である型より下(extends)であるか、または上であるか(super)を宣言すること
・境界はextendsとsuperを同時に指定することはできない
※superによる境界設定はワイルドカードで<? super B>といった表現をする場合のみ可能
利用例
2.型変数へのバインド
型変数へのバインド
・newする際の「new Hoge<String>();」
・この<String>が、クラス定義時に宣言したHoge型の「型変数T」にバインドされる
バインドするとどうなるの?
・上記ケースで言えば、String型以外は受け付けないし、String型を返すことが保証できるようになる
・new Hoge<String>のStringは「実型引数」と呼ばれる
2種類
・型変数へのバインド可能な場所は2種類
1.newするとき
2.継承するとき
1.new
new Hoge<String>();
・例1 … アダプタ生成// アダプタ生成
mAdapter = new ArrayAdapter<String>(
getApplicationContext(),
android.R.layout.simple_list_item_1,
VIEW_LIST_ITEMS);
・例2 … アダプタ生成
mAdapter = new ArrayAdapter<String>(getApplicationContext(),
R.layout.list_row, new ArrayList<String>());
プロパティにてジェネリックな型の変数を宣言後、ArrayAdapterクラスのオブジェクト生成
private ArrayAdapter<RecordItem> arrayAdapter = null;
arrayAdapter = new ArrayAdapter<RecordItem>(this,R.layout.listview);
選択項目に使用するユーザ定義クラスRange
・ジェネリクスを使用して指定
ArrayAdapter<Range> adapter = new ArrayAdapter<>(
this,
android.R.layout.simple_spinner_dropdown_item,
RANGES
);
2.継承
public class HogeEx extends Hoge<String> {}
3.変数の型の宣言
宣言できる場所
・ジェネリックな型の変数を宣言するとき
Hoge<String> hoge;
・ワイルドカードなどが使える▼例
private ArrayAdapter<String> mAdapter;
ArrayAdapter<String> mAdapter;
4=1+3.型変数での変数宣言
<>を伴わない
「型変数での変数宣言」は<>を伴わない
▼インスタンスフィールドを宣言する例
public class Hoge<T> {
private T t;
}
・インスタンスメソッド内でローカル変数として宣言することも出来る
・ただし、型変数はインスタンスごとに保持されるものだからstaticフィールドやメソッドではこれらの型変数は利用できない
Java ジェネリクスのポイント
Java7 ジェネリクスのダイアモンド・オペレータ
ジェネリックインスタンス作成のための型推定