Java汎用を再確認し、より深く理解し、よりよく使用することができます!

6945 ワード

1.引用
jdk5.0にJava汎用が導入され、エラーを低減し、タイプに追加の抽象層を追加することを目的としています.Javaの汎用性、汎用性の背後にあるターゲット、および汎用性を使用してコードの品質を向上させる方法について簡単に説明します.
2.なぜ汎用型を使うのですか.
JavaでIntegerを格納するリストを作成するシーンを想定します.コードは次のように書かれる可能性があります.
List list = new LinkedList();
list.add(new Integer(1)); 
Integer i = list.iterator().next();

驚くべきことに、コンパイラは最後の行を提示します.返されるデータ型がわかりません.したがって、コンパイラのプロンプトには、明示的な変換が必要です.
Integer i = (Integer) list.iterator.next();

リストの戻りタイプが整数であることを保証する約束はありません.定義されたリストには、任意のオブジェクトを含めることができます.コンテキストをチェックしてリストを取得したことしか知りません.タイプを表示する場合は、Objectのみが保証されるため、タイプが安全であることを確認するために明示的な変換が必要です.
この変換はうるさいかもしれませんが、このリストのデータ型が整数であることを知っています.変換すると、私たちのコードもめちゃくちゃになります.プログラマが明示的な変換でエラーを発生した場合、タイプに関連するランタイムエラーが投げ出される可能性があります.
プログラマが特定のタイプを使用する意図を表現し、コンパイラがこのタイプの正確性を確保できれば、これはより容易になります.
これが汎用型の背後にある核心思想である.
前のコード・セグメントの最初の行を次のように変更します.
List list = new LinkedList<>();

タイプを含む菱形演算子<>を追加することで、このリストの特化範囲をIntegerタイプに縮小します.つまり、リストに保存するタイプを指定します.コンパイラは、コンパイル時にこのタイプを強制的に実行できます.
小さなプログラムでは、これはわずかな追加のように見えます.しかし、より大きなプログラムでは、顕著な頑丈性を増加させ、プログラムをより読みやすくすることができる.
3.汎用方法
汎用メソッドは、単一のメソッドで記述を宣言するメソッドであり、異なるタイプのパラメータで呼び出すことができます.コンパイラは、使用するタイプの正確性を確保します.以下に、汎用メソッドのいくつかのプロパティを示します.
  • 汎用メソッドメソッド宣言の戻りタイプの前に、タイプパラメータ(ラップタイプの菱形演算子)
  • があります.
  • 型パラメータは、境界を有することができる(境界は、本明細書で後述する)
  • 汎用メソッドは、メソッド署名において
  • をカンマで区切る異なるタイプのパラメータを有することができる.
  • 汎用法の方法体は一般法と同様
  • である.
    配列をリストに変換する汎用メソッドの例を定義します.
    public  List fromArrayToList(T[] a) {   
        return Arrays.stream(a).collect(Collectors.toList());
    }

    前の例では、方法宣言の は、この方法が汎用タイプTを処理することを示す.メソッドがvoidを返しても、そうする必要があります.上記のように、メソッドは複数の汎用タイプを処理することができ、この場合、すべての汎用タイプをメソッド宣言に追加する必要があります.たとえば、上記のメソッドを変更してタイプTとタイプGを処理する場合は、次のように書く必要があります.
    public static  List fromArrayToList(T[] a, Function mapperFunction) {
        return Arrays.stream(a)
          .map(mapperFunction)
          .collect(Collectors.toList());
    }

    T型要素を持つ配列をG型要素を含むリストに変換する関数を渡しています.たとえば、IntegerをString表現に変換します.
    @Test
    public void givenArrayOfIntegers_thanListOfStringReturnedOK() {
        Integer[] intArray = {1, 2, 3, 4, 5};
        List stringList
          = Generics.fromArrayToList(intArray, Object::toString);
     
        assertThat(stringList, hasItems("1", "2", "3", "4", "5"));
    }

    Oracleでは、汎用タイプを大文字で表すことを推奨し、JavaコレクションでTがタイプ、Kがキー、Vが値を表すなど、より記述的なアルファベットを選択して形式タイプを表すことを推奨します.
    3.1.はんけいきょうかい
    前述したように、タイプパラメータは境界付きであってもよい.境界は「制限」を意味し、メソッドが許容できるタイプを制限することができます.
    たとえば、メソッドがタイプとそのすべてのサブクラス(上限)を受け入れるか、タイプがすべてのスーパークラス(下限)を受け入れるかを指定できます.
    上界タイプを宣言するには、タイプの後にキーワードextendsを使用し、使用する上限に従います.例:
    public  List fromArrayToList(T[] a) {
        ...
    }

    ここではキーワードextendsを用いてタイプT拡張クラスの上限を表したり,インタフェースの上限を実現したりする.
    3.2. 複数の境界
    タイプには、次のように複数の上界があります.

    T拡張タイプの1つがクラス(すなわちNumber)である場合、境界リストの1番目に配置する必要があります.そうしないと、コンパイル時にエラーが発生します.
    4.ワイルドカードの使用
    ワイルドカードはJavaで疑問符「?」で表され、未知のタイプを指すために使用されます.ワイルドカードは汎用を使用する場合に特に役立ち、パラメータタイプとして使用できますが、まず重要な注釈です.
    ObjectはすべてのJavaクラスのスーパータイプであることはよく知られていますが、Objectの集合は任意の集合のスーパータイプではありません.(ちょっと回り道かもしれませんが、みんなよく細かくしてください)
    たとえば、List List のスーパータイプでは、List Listの がコンパイラエラーを き こします.
    これは、 タイプを じコレクションに するときに する を するためです.
    じルールは、タイプとそのサブタイプの の に されます.この を てみましょう.
    public static void paintAllBuildings(List buildings) {
        buildings.forEach(Building::paint);
    }

    サブタイプBuilding、インスタンスHouseを すると、HouseがBuildingのサブタイプであっても、このメソッドをHouseリストと に することはできません.このメソッドをタイプ およびそのすべてのサブタイプとともに する がある は、 の を できます.
    public static void paintAllBuildings(List extends Building> buildings) {
        ...
    }

    これで、この はBuildingタイプとそのすべてのサブタイプを することができます.これを ワイルドカードと び、タイプBuildingが です.
    ワイルドカードでは、 のタイプが したタイプのスーパータイプである がある も できます. は、superキーワードの に のタイプとともに できます.たとえば、 super T>は、T(=Tおよびそのすべての )のスーパークラスである のタイプを します.
    5.タイプ
    がJavaに され、タイプが であることを し、 が にオーバーヘッドを しないことを します.コンパイラはコンパイル にtype erasureというプロセスを に します.
    タイプ では、すべてのタイプのパラメータが され、 がない はObjectに き えられます.したがって、コンパイルされたバイトコードは、 のクラス、インタフェース、およびメソッドのみを み、 しいタイプが されないことを します.コンパイル にObjectタイプにも しい が されます. に、 タイプの を します.
    public  List genericMethod(List list) {
        return list.stream().collect(Collectors.toList());
    }

    タイプ を すると、 に すように、 タイプTがObjectに き えられます.
    // for illustration
    public List withErasure(List list) {
        return list.stream().collect(Collectors.toList());
    }
     
    // which in practice results in
    public List withErasure(List list) {
        return list.stream().collect(Collectors.toList());
    }

    タイプが がある は、コンパイル にそのタイプがバインディングに き えられます.
    public  void genericMethod(T t) {
        ...
    }

    コンパイル に されます.
    public void genericMethod(Building t) {
        ...
    }

    6. および のデータ
    Javaにおける の1つの は,タイプパラメータが タイプではないことである.
    たとえば、 の はコンパイルできません.
    List list = new ArrayList<>();
    list.add(17);

    のデータ がなぜ しないのかを するには、 がコンパイル の であることを えておく があります.これは、タイプが され、すべての がObjectクラスとして されることを します. を げて、リストのaddメソッドを てみましょう.
    List list = new ArrayList<>();
    list.add(17);

    addメソッドの は のとおりです.
    boolean add(E e);

    のようにコンパイルされます.
    boolean add(Object e);

    したがって、タイプパラメータはObjectに できる があります. タイプはObjectから されないため、タイプパラメータとして することはできませんが、Javaは タイプと と を します.
    Integer a = 17;
    int b = a;

    したがって、 を できるリストを するには、パッケージを します.
    List list = new ArrayList<>();
    list.add(17);
    int first = list.get(0);

    コンパイルされたコードは のとおりです.
    List list = new ArrayList<>();
    list.add(Integer.valueOf(17));
    int first = ((Integer) list.get(0)).intValue();

    Javaの のバージョンでは、 のデータ を に できます.Valhallaプロジェクトは、 な を することを としている.JEP 218に する な を することが えられる.
    7.まとめ
    Java はJava の な であり、プログラマーの を にし、エラーを こしにくいからです. はコンパイル にタイプの を し、 も なのは、アプリケーションに のオーバーヘッドをもたらすことなく、 アルゴリズムを することです.
    がいいと うなら、 に してください. の の さんのブログ