Javaの型消去で

このブログは、Javaの型消去の定義を紹介し、詳細にJavaで消去表示されたシーンのタイプを記述する。

1.型消去は何ですか

あなたの型消去の迅速な印象を与えるために、最初は非常に簡単で、非常に古典的な例を与えます。

// 指定泛型为String
List<String> list1 = new ArrayList<>();
// 指定泛型为Integer
List<Integer> list2 = new ArrayList<>();

System.out.println(list1.getClass() == list2.getClass()); // true
复制代码

上記の判定結果true同じタイプになり、最終的にはArrayListのにコンパイル二つの異なる一般的なリストを表現するために導入された、オリジナルの汎用パラメータ文字列と整数のは消去されます。これは、消去のタイプの典型的な例です。

そこにあるのはなぜ我々は型消去について話す場合と、私たちは、ジェネリックを理解する必要があります。

2.ジェネリック

2.1。一般的な定義

2004年9月30日で、プロジェクトコードネームタイガーJDK 1.5はあなたに会うためにジェネリックから、離します。JDK 1.5が使用するJava構文のしやすさに大きな改善を行いました。ジェネリックに加えて、リリースならびにように自動包装、動的なアノテーション、列挙、可変長パラメータ、foreachループとを添加しました。

バージョン1.5より前では、順番に、Javaクラスは、汎用性の高いパラメータ型を作成し、返すようなタイプは、通常はありません必要なタイプの場合、それは適切な場所に、その出演者、プログラムが必要であり、可視オブジェクトに設定されています正常に動作することができ、非常に面倒、少し注意が間違っていることでしょう。

パラメータ化された型は基本的に汎用的です。これは、データ型は、パラメータとして指定されています。ジェネリック医薬品の導入、それが良い何ですか?

実行時にJDK 1.5をコンパイルし、事前に見つけることができる前に、ジェネリック医薬品が間違っている可能性があります。言い換えれば、一般的な検出メカニズムは、タイプセーフなコンパイル時間を提供します。たとえば、Integer型の変数は、元々、コードはこれに動作し、エラーを報告します場合にのみ、ジェネリック医薬品を使用せずに、私たちはコードで設定文字列になりました。

そして、この問題は、ジェネリック医薬品の導入後に発生しません。あなたは、コンパイル時に、コンプライアンスタイプのために、そのタイプを決定し、ジェネリックパラメータで指定されたタイプを知ることができるからです。

3件の汎用利用があり、それはクラス、インタフェースおよび方法で使用されています。

3.ジェネリックの使用

3.1ジェネリッククラス

ジェネリッククラスの定義3.1.1

次のように単純なジェネリッククラスを定義することができます。

public class Generic<T> {
    T data;
    
    public Generic(T data) {
        setData(data);
    }
    
    public T getData() {
        return data;
    }
    
    public void setData(T data) {
        this.data = data;
    }
}
复制代码

Tは、パラメータのタイプを表し、任意の型を表します。もちろん、それだけでみんなコンベンション習慣だ、Tを記述する必要はありません。上記の一般的なクラスでは、我々は次のように使用のようにすることができます。

3.1.2ジェネリッククラスを使用します

// 假设有这样一个具体的类
public class Hello {
    private Integer id;

    private String name;

    private Integer age;

    private String email;
}

// 使用泛型类
Hello hello = new Hello();
Generic<Hello> result = new Generic<>();
resule.setData(hello);

// 通过泛型类获取数据
Hello data = result.getData();
复制代码

もちろん、一般的なクラスはジェネリッククラスメソッドやフィールドを入力し、指定されたタイプを渡していない場合は、印刷すると、任意の型として定義することができresult.getClass()、それを取得しますGeneric

3.2。一般的な方法

3.2.1ジェネリックメソッドの定義

まず、構造体として定義することができる戻り値のない一般的な方法を見てください。

// 定义不带返回值的泛型方法
public <T> void genericMethod(T field) {
    System.out.println(field.getClass().toString());
}

// 定义带返回值的泛型方法
private <T> T genericWithReturnMethod(T field) {
    System.out.println(field.getClass().toString());
    return field;
}
复制代码

3.2.2一般的なメソッドを呼び出します

// 调用不带返回值泛型方法
genericMethod("This is string"); // class java.lang.String
genericMethod(56L); // class java.lang.Long

// 调用带返回值的泛型方法
String test = genericWithReturnMethod("TEST"); // TEST class java.lang.String
复制代码

戻り値の方法は、Tは、現在の関数の戻り値の型です。

3.3。一般的なインタフェース

次のようにジェネリックインターフェイスが定義されています

public interface genericInterface<T> {
}
复制代码

ジェネリッククラスと同様の方法、ここではそれらを繰り返すありません。

4.ジェネリックワイルドカード

一般的なワイルドカードとは何ですか?公式の説明は、ということです

不明のタイプ。

つまり、ワイルドカードは任意の型を表すことができる不定です。3つの使用方法もありますが、<?>、<?T拡張>と<?スーパーT>。

今、あなたは、このようなTのいずれかのタイプを表すためにワイルドカードを持っていることを、なぜ我々はやるな不定のワイルドカードが必要なのでしょうか?主な問題は、継承された一般的な問題によって引き起こされているため。

4.1。ジェネリックの継承

例えば、初めて目に

List<Integer> integerList = new ArrayList<>();
List<Number> numberList = integerList;
复制代码

私たちは、それは知っているIntegerから継承されNumberたクラス。

パブリック最終クラス整数の数が同等実装延び{...}

そして、上記のコードは、それをコンパイルすることができますか?確かに動作しません。整数の番号がリストとリストの間の継承関係から、そこに継承されることを意味するものではありません。それはどのようなワイルドカードのシナリオのですか?

4.2。ワイルドカードアプリケーションシナリオ

JavaScriptなどの他の機能では、関数のパラメータは、変換の任意の種類を必要とすることなく、任意のタイプのものであってもよいので、いくつかのアプリケーションシナリオでは、この関数は、汎用性の高いであろう。

この強く型付けされたJava言語では、パラメータの型の機能が固定されています。だから、あなたがそれを行う方法をJavaでJavaScriptなどと同様の一般的な機能を実現したい場合は?私たちはジェネリックワイルドカードを必要とする理由です。

例えば、犬、豚や猫三つのクラス、私たちはすべての動物の足のリストで動物の合計数を計算するための一般的な機能を持っている必要がありますようにJavaであれば、我々は、動物の多くを持っていると仮定し、どのようにそれを行うには?

一部の人々は、ああ、この問題に対する一般的な解決策がないジェネリックと主張するだろうか?ジェネリック医薬品は、特定のタイプを指定する必要があります。ジェネリック医薬品は解決できない...公式はジェネリックのワイルドカードを提案したため。

4.3。無制限ワイルドカード

無制限ワイルドカードです?あなたはそれを見るために求めることができる、Tが行う好きではないのですか?なぜ彼らは前方に入れているんでした?彼らの主な違いは、Tは、主に主に使用ジェネリッククラスとジェネリックメソッドのために?ジェネリッククラスやメソッドを宣言するために使用される、ということです。以下は簡単な例を与えます。

// 定义打印任何类型列表的函数
public static void printList(List<?> list) {
    for (Object elem: list) {
        System.out.print(elem + " ");
    }
}

// 调用上述函数
List<Integer> intList = Arrays.asList(1, 2, 3);
List<String> stringList = Arrays.asList("one", "two", "three");
printList(li);// 1 2 3 
printList(ls);// one two three
复制代码

オブジェクト関数の上には、リストのいずれかのタイプを印刷することです。あなたは、関数の内部で、次のことができ、最後にあるもののリストジェネリック型を気にしませんでした<?>の理解だけ残して、特定の要素を追加する機能が削除され、読み取り専用機能を提供して見ることができます機能の特定のタイプの独立しました。上記の例から分かるように、何も気にしない以外、要素の数とそれらの唯一の懸念は、空です。

そして、一方Tは、我々はまた、一般的なメソッドを定義する方法を示していますし、ジェネリックメソッドが呼び出されます。内部の一般的な方法は、特定のタイプではなく、空とそう単純ではないだけの数を気にする予定です。

4.4。上限ワイルドカード<?Tは拡張します>

それはので、?任意の型を表すことができ、その後、拡張し、それをしているのですか?

そのような需要があると仮定すると、我々は、特定のタイプは、(例えば、すべての動物のクラスとその派生クラス)私たちの関数を呼び出すことができますが、今の使用のみを許可する?すべての種類は、私たちのニーズを満たすことができない、関数を呼び出すことができます。

private int countLength(List< ? extends Animal> list) {...}
复制代码

パブリック関数を完了するために、上限ワイルドカードを使用した後、次の方法を使用してそれを呼び出すことができます。

List<Pig> pigs = new ArrayList<>();
List<Dog> dogs = new ArrayList<>();
List<Cat> cats = new ArrayList<>();

// 假装写入了数据
int sum = 0;
sum += countLength(pigs);
sum += countLength(dogs);
sum += countLength(cats);
复制代码

例を読んだ後、私たちは、単純な結論を引き出すことができます。それは、上の任意の特定の型にバインドワイルドカードで、ワイルドカード派生クラスの特定のタイプを処理することができます。

いくつかは、私は上記の例を組み合わせて、少し無知な力を見ても、単純な人間の言葉を説明する:ワイルドカードの上限は、動物が箱を置くことができるものです。

4.5。下界ワイルドカード<?スーパーアニマル>

私たちは、それは未知のタイプの制限です(すべての動物のサブクラス、および上述された動物)は、その特定の種類の特定のタイプまたはサブタイプで、上限ワイルドカードの上におしゃべり。そして下限ワイルドカードは、未知のタイプの特定のタイプまたは特定のタイプのスーパータイプ、すなわち基底クラスまたはスーパークラスに限定されるものであろう。

上限ワイルドカードでは、我々は、例を挙げました。動物のいずれかの種類を扱うことができる機能を書き、動物は、クラスの派生クラスです。そして今、我々はスーパークラスのすべての機能を処理するための関数を書きたい整数整数です。

public static void addNumbers(List<? super Integer> list) {
    for (int i = 1; i <= 10; i++) {
        list.add(i);
    }
}
复制代码

型消去

使用するのは簡単いくつかのジェネリック医薬品を簡単に理解した後、私たちはこのブログの主題に戻り次第です - 型消去。上に挙げたいくつかの一般的な利点が、一般的なライフサイクルがありますが、コンパイル・フェーズに限定されています。

この資料の冒頭は、典型的な例を与えることであるサンプル。私たちは、一般的な結果を検出した後、消去動作中にコンパイルコンパイル後総称化するための措置は、一般的な情報になりますがかかります。冒頭で言及した記事のほとんどの例のように、我々は簡単な例を与えるために上記で定義されたジェネリックジェネリッククラスを使用します。

Generic<String> generic = new Generic<>("Hello");
Field[] fs = generic.getClass().getDeclaredFields();
for (Field f : fs) {
    System.out.println("type: " + f.getType().getName()); // type: java.lang.Object
}
复制代码

getDeclaredFields現在のクラスの様々なフィールドを取得できる反射法は保護され、民間、公共を含め、宣言されています。

私たちは、着信Stringの汎用化が消去され、オブジェクトに置き換えられている見ることができます。それはどこの文字列と整数の行く前に、一般的な情報?たぶん、あなたが点滅します。この時間は、それがすべてではない、一般的なオブジェクトは、それの後に消去さになるのだろうか?心配しないで、お読みください。

我々は将来的には一般的なトップ上限ワイルドカードを使用する場合、何が起こるのだろうか?以下のフォームに我々ジェネリッククラス。

public class Generic<T extends String> {
    T data;

    public Generic(T data) {
        setData(data);
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}
复制代码

そして、ジェネリック型を消去した後、再度確認するためにリフレクションを使用しています。コンソール出力type: java.lang.Stringあなたは、一般的な消去、我々は一般的なクラスに上限を設定した場合、それはタイプの上限後に交換され、見ることができます。指定されていない場合は、統一されたオブジェクトに置き換えられます。従って、一般的なタイプの方法は、同様にクラスで定義されました。

6.最後に書かれました

あなたは問題の記事は、翼に歓迎していることが判明した場合、私は速やかに訂正します。

参考:

  1. Java言語の型消去
  2. 下界通配符
  3. 一覧<?>とListの違い

おすすめ

転載: juejin.im/post/5ceba1a2f265da1b95703558