Javaのジェネリック型の利点は、第一に、コードの再利用性と汎用性が向上すること、第二に、型に特定の制約を追加し、コンパイラがそれをチェックすることで、自動的に暗黙のうちに型を変換できることです。ジェネリックについて理解すべき概念がいくつかあります。
I. 一般的なワイルドカード
一般的なワイルドカードT、E、K、V、?など、本質的にこれらのワイルドカードは、違いはありませんが、26大文字も直接使用することができ、タイプに代わって、ちょうど上記の開発、特定の意味を与えるためにこれらのワイルドカードに慣れている。
T一般に、String、IntegerなどのJava型や、Fruit、Appleなどのクラスを表すのに使われる。
E要素の一般的な表現
KV: KVは一般的に、キーと値のペアの組み合わせを示すために併用される。
?はJavaの不定型を表す
ジェネリック・メソッドとジェネリック・クラス、ジェネリック・インターフェイス
1汎用クラスと汎用インターフェース
//ここでは、Tは任意のロゴとして書くことができ、T、E、K、Vなどの一般的なパラメータや、その他の形式のパラメータが、一般化された型を表すためによく使われる。
//ジェネリック・クラスをインスタンス化する際には、特定のT
public class Generic<T>{
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey(){
return key;
}
}
// インスタンス化するとき、型を直接指定することができる。
Generic<Integer> genericInteger = new Generic<Integer>();
// 汎用インターフェース
public interface Generator<T> {
public T method();
}
// 型を指定しない実装は、インスタンス化されたときに型を指定する。
class GeneratorImpl<T> implements Generator<T>{
@Override
public T method() {
return null;
}
}
// 指定された型の実装
class GeneratorImpl implements Generator<String>{
@Override
public String method() {
return "hello";
}
}
2一般化された方法
静的型パラメーターをジェネリックの上に追加することはできない。主な理由は、後述する型消去による型消去のためで、ジェネリックは使えないが、静的メソッドは使える。
public static T params; // これは許されない、非常に許されない、例外がスローされる!
public <T> T showTime(T t){
return t;
}
public static <T> T showTime(T t){
return t;
}
public static <T> T showTime<Class<T> t>{
return t.newInstance();
}
III.ヒント消去
Javaジェネリックは擬似ジェネリックであり、Javaコンパイル時にすべてのジェネリック情報が消去される。
PECSはジェネリック型と呼ばれ、この処理はジェネリック消去と呼ばれる。これを使用する過程で、ジェネリック型の情報を再度追加して変換し直すことになる。
一般的な消去をテストするには?
1定義されたジェネリック型が存在するかどうか、Javaのコンパイル済みバイトコードをチェックする。
2一般化された消去をテストするコードの小さな例を通して、PECSの原理を説明する。
例1:配列リストを使って
ArrayList<String> list1 = new ArrayList<String>();
ArrayList<Object> list2 = new ArrayList<Object>();
list1.getClass == list2.getClass() //true
例2:反射的判断力を使う
ArrayList<String> list1 = new ArrayList<String>();
list1.add(“123"); // 文字列型しか書けない、他の型は例外を投げる
list1.getClass().getMethod("追加",Object.class).invoke(list1,); // リフレクションによって追加されたデータは問題ない。
型はコンパイル後に消去される。
IV.PECS
上界ワイルドカードと下界ワイルドカードの説明では、まず原理として、PECSの原理である
Producer Extends: ストレージコンテナをプロデューサーとして扱い、そこからデータを取り出し続けるのであれば、Extendsを使う必要がある。
読み取り専用、書き込み不可、リストを使う<? extends Fruit>:Producer
Consumer Superもしコンテナが消費者であり、コンテナ内部に格納されたデータであるならば、Superの使用は、コンテナ内のデータを消費することになる。
書き込み専用で読めない、リストを使う<? super Apple>:Consumer
V. 上限ワイルドカードのスーパーと下限ワイルドカードの拡張
2つのワイルドカードを説明するために、直接例を挙げる:
在List<? extends Fruit>ジェネリックコレクションは、要素の型については、コンパイラは、要素が果実から継承されていることを知ることができる、具体的には、果実のサブクラスであり、知ることは不可能であるため、ジェネリックコレクションへの要素の挿入は、特定の型を知ることができないコンパイルされていない!.しかし、要素がFruitから継承されることがわかっているので、この一般的なコレクションからFruit型の要素を取り出すことは可能である。.
リストについて説明する<? extends Fruit> 意味:extendsは上界ワイルドカード、Listは下界ワイルドカードである。<? extends Fruit> 以下の可能性があることを示す。 List<Apple>,
リスト<Banana>PECSの原則は以下の通りである。<Orange> List<Fruit> しかし、データを取り出しても何の影響もない。すべての型はFruit型から継承されるので、PE原則に従って、この一般的なコレクションからデータを取り出すことができる。
在List<? super Apple>ジェネリック・コレクションでは、要素の型はAppleの親であるが、具体的にどの親であるかはわからないので、要素を読むときにどの親を読むかを決定することはできない。.要素を挿入するとき、このコレクションの要素はすべてAppleの親クラスなので、AppleとAppleのサブクラスを挿入することができる。.
説明:リスト<? super Apple>: はこのコレクションの型を示し、AppleまたはAppleの親クラスであれば、Listである可能性がある。<Apple>,List<Fruit>,List<Food>,またはリスト<Object>などのように、AppleやAppleのサブクラスであれば、この中にデータを入れることはできるが、取り出すときに、どの親を取ればいいのかわからないという問題があり、型消去後は、Objectしか取れないので、superは書き込むことはできるが、読み出すことはできないという、CSの原則に沿ったものになっている。
このようなワイルドカードの使い道は何なのか、私は少し知っている:例えば、キューを開きたい場合、プロデューサーはそのキューにしかデータを書き込めず、コンシューマーはそのキューからしかデータを取り出せない。
プロデューサ側は、このsuperをオープンにして、プロデューサはその中のリストにしかデータを書き込めない。
extendsこの場合、コンシューマはデータを受け取ることができるだけで、データを書き戻すことはできない。
MATT
実際、一般的な型の最初の頃は、特にこのextendsとsuperを理解するのは本当に難しい。
この作品の使い方だが、引き続き考えていく必要がある。