ジェネリックスに対応した型を、自分で定義することもできます。以下は、HashMapクラス(java.utilパッケージ)のコードを抜粋したものです。
- public class HashMap<K,V> ~ {
- transient Node<K,V>[] table;
- public V put(K key, V value) {
- ...
- }
- }
ジェネリック対応のクラスでは、クラス名にも受け取るための型パラメータを指定しなければなりません。HashMapの例であれば「HashMap<K,V>」の部分です。これでK、Vという2つの型(この場合であれば、キーと値の型)を受け取れることを意味します。
型パラメータは、慣例的に大文字一文字で表すのが普通です。E(要素)、K(キー)、V(値)などが良く使われます。
型パラメーターは、以下の場所から参照できます。
- インスタンスメンバー(static修飾子の付かないメンバー)の引数
- インスタンスメンバーの戻り値型
- インスタンスメンバー配下のローカル変数
境界ワイルドカード型
ジェネリックスを利用する際に、不変というキーワードを押さえておくことは重要です。たとえば以下のようなコードは、ジェネリックスの世界では不可です。
- List<Number> list = new ArrayList<Long>();
NumberクラスはLongクラスのスーパークラスですが、ジェネリックスではこのような狭い型から広い型への変換も許可しません。型の一致を厳密にチェックするのです(不変の意味です)。
しかし、これは厳密にすぎて使いにくい場合があります。そこでもう少し幅を持った型の指定を可能にしたのが、境界ワイルドカード型です。境界ワイルドカード型では、<? extends E>の形式で型を指定します。これによって、型Eと、その派生クラスを認めるという意味になります。
例えば以下は、ArrayListクラスのaddAllメソッドの例です(addAllはコレクションを配列に追加します)。
- public boolean addAll(Collection<? extends E> c) {...}
境界ワイルドカード型を利用することで、たとえばArrayList<Number>型のaddAllメソッドに対して、List<Long>型の値を渡すことが可能になります。
下限境界ワイルドカード
本文で示した境界ワイルドカード型は、指定された型を上限に、その派生クラスを許容することから上限境界ワイルドカード型とも呼びます。一方、(それほど利用頻度はおおくないものの)指定された型を下限に、そのスーパークラスを許容する下限境界ワイルドカード型もあります。<? super E>のように、superキーワードで表します。
関連ページ
監修