いわゆるジェネリック: クラスとインターフェイスを定義するときに型パラメーターを指定できます。この型パラメーターは、変数を宣言したりオブジェクトを作成したりするときに決定されます (つまり、型引数とも呼ばれる実際の型パラメーターを渡します)。
汎用クラスまたはインターフェイス
「ダイヤモンド」構文のコピー コードは次のとおりです。
//意味
パブリック インターフェイス List<E> は Collection<E> を拡張します
public class HashMap<K,V> extends AbstractMap<K,V> 実装 Map<K,V>、クローン可能、シリアル化可能
//使用
List<String> list = new ArrayList();
//Java 7 以降では、後ろの山括弧の type パラメータを省略できます。
List<String> list = new ArrayList<>();
ジェネリック クラスからサブクラスを派生する
次のようにコードをコピーします。
//方法1
パブリック クラス App extends GenericType<String>
//方法2
パブリック クラス App<T> は GenericType<T> を拡張します
//方法3
パブリック クラス App は GenericType を拡張します
疑似ジェネリック
実際のジェネリック クラスは存在せず、JVM はジェネリック クラスの存在を認識しません。つまり、JVM は通常のクラスと何ら変わりなく処理します。静的メソッド、静的初期化ブロック、静的変数。
- 以下の方法はすべて間違っています。 コードのコピーは次のとおりです。
プライベート静的 T データ。
静的{
Tf;
}
パブリック静的無効関数(){
T 名 = 1;
}
次の例では、ジェネリック クラスが存在しないことを側から確認できます。コードは次のとおりです。
public static void main(String[] args){
List<String> a1 = 新しい ArrayList<>();
List<Integer> a2 = 新しい ArrayList<>();
System.out.println(a1.getClass() == a2.getClass());
System.out.println(a1.getClass());
System.out.println(a2.getClass());
}
出力されたコピーコードは次のとおりです。
真実
クラスjava.util.ArrayList
クラスjava.util.ArrayList
ワイルドカードを入力してください
まず、Foo が Bar の親クラスであるが、List<Foo> が List<Bar> の親クラスではないことを明確にする必要があります。さまざまな汎用親クラスを表すために、Java は「?」を使用します。つまり、List<?> はさまざまなジェネリック リストの親クラスを表します。この種のワイルドカードでは、リスト ジェネリックは要素を設定 (set) することはできませんが、要素を取得 (get) することしかできません。プログラムはリスト内のタイプを判断できないため、オブジェクトを追加できません。ただし、取得されるオブジェクトは Object 型である必要があります。
次のメソッドはコンパイル時にエラーが発生します。
次のようにコードをコピーします。
リスト<?> リスト = new ArrayList<>();
list.add(新しいオブジェクト());
いくつかのアイデア:
1. List<String> オブジェクトは List<Object> オブジェクトとして使用できません。つまり、List<String> クラスは List<Object> クラスのサブクラスではありません。
2. 配列はジェネリックとは異なります。Foo が Bar のサブタイプ (サブクラスまたはサブインターフェイス) であると仮定すると、Foo[] は依然として Bar[] のサブタイプですが、G<Foo> は G<Bar> サブタイプではありません。
3. さまざまなジェネリック リストの親クラスを表すには、型ワイルドカードを使用する必要があります。型ワイルドカードは、List コレクションに型引数として渡す必要があります。List<? > (型要素の不明なリストを意味します)。この疑問符 (?) はワイルドカード文字と呼ばれ、その要素の型は任意の型に一致します。
ワイルドカードの上限
List<? extends SuperType> は、すべての SuperType ジェネリック リストの親クラスまたはそれ自体を表します。ワイルドカードの上限を持つジェネリックスは set メソッドを持つことができず、get メソッドのみを持つことができます。
ワイルドカードの上限を設定すると、次の問題を解決できます。 Dog は Animal のサブクラスであり、受信リストの数を取得する getSize メソッドがあります。 コードは次のようにコピーします。
抽象クラス 動物 {
パブリック抽象 void run();
}
クラス Dog extends Animal {
public void run() {
System.out.println("ドッグラン");
}
}
パブリック クラス App {
public static void getSize(List<Animal> list) {
System.out.println(list.size());
}
public static void main(String[] args) {
List<Dog> list = new ArrayList<>();
getSize(list); // ここでコンパイルエラーが発生しました
}
}
ここでのプログラミング エラーの理由は、List<Animal> が List<Dog> の親クラスではないことです。一つ目の解決策は、getSizeメソッドの仮引数List<Animal>をList<?>に変更することですが、この場合、オブジェクトを取得するたびに強制的な型変換が必要となり面倒です。ワイルドカードの上限を使用すると、List<Animal> を List<? extends Animal> に変更でき、コンパイル時にエラーが発生せず、型変換も必要ありません。
ワイルドカードの下限
List<? super SubType> は、SubType 汎用リストの下限を表します。ワイルドカードの上限を持つジェネリックには get メソッドを含めることはできず、set メソッドのみを含めます。
一般的なメソッド
型パラメーターを使用せずにクラスとインターフェイスを定義するが、メソッドを定義するときに型パラメーターを自分で定義したい場合、JDK1.5 ではジェネリック メソッドのサポートも提供されます。ジェネリック メソッドのメソッド シグネチャには、通常のメソッドのメソッド シグネチャよりも多くの型パラメータ宣言が含まれます。複数の型パラメータ宣言は、すべてのメソッドの間にカンマ (,) で区切られます。修飾子とメソッドの戻り値の型の構文形式は次のとおりです。
次のようにコードをコピーします。
修飾子の戻り値の型メソッド名(型リスト) {
//メソッド本体
}
ジェネリック メソッドでは、型パラメーターを使用して、メソッドの 1 つ以上のパラメーター間、またはメソッドの戻り値とパラメーター間の型の依存関係を表現できます。このような型の依存関係がない場合は、ジェネリック メソッドを使用しないでください。コレクションのコピー メソッドは汎用メソッドを使用します。
次のようにコードをコピーします。
public static <T> void copy(List<? super T> dest, List<? extends T> src){ ...}
このメソッドでは、src 型が dest 型またはそれ自体のサブクラスである必要があります。
消去して変換する
厳密なジェネリック コードでは、ジェネリック宣言を持つクラスには常に型パラメータが必要です。ただし、古い Java コードとの一貫性を保つために、型パラメータを指定せずにジェネリック宣言を持つクラスを使用することもできます。このジェネリック クラスに型パラメーターが指定されていない場合、型パラメーターは raw 型と呼ばれ、パラメーターの宣言時に指定された最初の上限型がデフォルトになります。
ジェネリック情報を持つオブジェクトがジェネリック情報のない別の変数に代入されると、山かっこ内のすべての型情報が破棄されます。たとえば、List<String> 型を List に変換すると、List のコレクション要素の型チェックが型変数 (つまり Object) の上限になります。この状況を消去と呼びます。
サンプルのコピーコードは次のとおりです。
クラス Apple<T extends Number>
{
Tサイズ;
publicApple()
{
}
パブリックアップル(Tサイズ)
{
this.size = サイズ;
}
public void setSize(Tサイズ)
{
this.size = サイズ;
}
public T getSize()
{
this.size を返します。
}
}
パブリック クラス ErasureTest
{
public static void main(String[] args)
{
Apple<Integer> a = new Apple<>(6);
// a の getSize メソッドは Integer オブジェクトを返します
整数 = a.getSize();
// 山括弧内の型情報を失い、オブジェクトを Apple 変数に代入します
アップル b = a;
// b はサイズのタイプが Number であることだけを知っています
数値 size1 = b.getSize();
//以下のコードはコンパイルエラーが発生します
整数 size2 = b.getSize(); // ③
}
}