blog

Javaジェネリック

Javaのジェネリック型の利点は、第一に、コードの再利用性と汎用性の向上、第二に、コンパイラが自動的かつ暗黙的に型を変換するためにチェックできる、型に対する特定の制約の追加です。ジェネリック型を理解す...

Mar 23, 2020 · 5 min. read
Share this

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を理解するのは本当に難しい。 この作品の使い方だが、引き続き考えていく必要がある。
Read next

Vueビルディングホイール

使いやすさや美しさよりも使いやすさを優先すべき chai.jsはTDD/BDD/アサーションライブラリのセットです。

Mar 23, 2020 · 3 min read