クレイジーJava講義ノート-第9章汎用型


第9章汎用型


9.1汎用入門


9.1.1コンパイル時にタイプの異常をチェックしない

List list = new ArrayList();
list.add("abc");
list.add(3);
for(int i = 0; i < list.size(); i++) {
  String str = (String) list.get(i);  //(1)
}

コンテナに汎用型を付けず、コンテナからエレメントを取り出したときにタイプ変換を行う場合、(1)タイプ強制変換異常ClassCastExceptionを報告することがあります.

9.1.2手動によるコンパイル時のチェックタイプ


リストクラスラップArrayList追加addメソッドを作成し、メソッドパラメータに追加可能なタイプを追加します.弊害は、複数のListサブクラスを手動で作成することです.

9.1.3汎用型の使用

List<String> list = new ArrayList<>();
list.add("abc");
list.add(3); //(1)

括弧の中間タイプリストには、指定したタイプをStringとしてのみ装着でき、他のタイプを装着するとエラーが報告され、(1)コンパイラがエラーを報告します.

9.1.4 Java 7汎用型の「菱形」構文

List<String> list = new ArrayList<String>(); //(1)
List<String> list = new ArrayList<>(); //(2)

(1)はJava 7以前の表記,(2)はJava 7以降の表記である.

9.2深い汎用性


9.2.1汎用インタフェース、クラスの定義

public interface List<E> {
  void add(E e);
  ...
}

public class Apple<T> {
  private T info;

  public Apple(T info) {
    this.info = info;
  }

  public void setInfo(T info) {
    this.info = info;
  }

  public T getInfo() {
    return this.info;
  }

  public static void main(String[] args) {
    Apple<String> apple = new Apple<>("abc");
  }
}

上記の例では、汎用性のあるインタフェースとクラスをそれぞれ宣言し、使用時に汎用性Tを実際のタイプに変更すればよい.

9.2.2汎用系からの出産


汎用宣言のクラスまたはインタフェースから子または子インタフェースを割り当てる場合は、次のように、親汎用の特定のタイプを指定します.
public class A extends Apple<T> {

}

コンパイラがエラーを報告します.クラス、インタフェース、メソッドを定義するときにタイプパラメータを宣言します.クラス、インタフェース、メソッドを使用する場合は、タイプパラメータが実際のタイプに入力されるべきです(クラスまたはインタフェースは実際のタイプのパラメータに入力されません).Apple系から子系を派遣すると、次のコードに変更されます.
// Apple T String 
public class A extends Apple<String> {

}

メソッドを呼び出すときは、すべてのデータパラメータ値を入力する必要があります.呼び出しメソッドとは異なり、クラス、インタフェースを使用するときに、実際のタイプパラメータを入力しないことができます.つまり、次のコードも正しいです.
public class A extends Apple

Appleから子クラスを派遣し,親メソッドを書き換える際には,汎用的な実際のタイプに注意し,メソッドの戻りタイプとパラメータは実際のタイプと一致しなければならない.

9.2.3汎用クラスは存在しない

  • 同じ汎用クラスのオブジェクトは、同じクラスによって生成されます.
  • 静的メソッド、静的初期化ブロック、または静的変数の宣言および初期化では、クラスが静的初期化コードをロードするときに実際のタイプを決定するため、タイプパラメータの使用は許可されません.
  • instanceof演算子の後に汎用クラスを使用することはできません.汎用はコンパイル期間チェックであり、コンパイル後に汎用が消去されるため、runtime期間中は次のcs汎用がStringであることを保証できません.そのため、
  • の使用は禁止されています.
    Collection cs = nwe ArrayList<String>();
    if(cs instanceof List<String>) {...} //  
    if(cs instanceof List) {...} //  
    

    9.3型ワイルドカード

    public void test(List c) {
      ...
    }
    
  • Listインタフェースを使用すると、実際のタイプのパラメータが入力されず、汎用的な警告が発生します.
  • ListオブジェクトはListオブジェクトとして使用できません.すなわち、ListクラスはListクラスのサブクラスではありません.
  • FooがBarのサブタイプ(サブクラスまたはサブインタフェース)である場合、Foo[]はBar[]のサブタイプであり、Gは汎用宣言を有するクラスまたはインタフェースであり、GはGのサブタイプではない.
  • Java汎用設計原則:コードがコンパイル時に警告が発生しない限り、実行時ClassCastException異常は発生しません.

  • 9.3.1タイプワイルドカードの使用


    タイプワイルドカードは?要素タイプは、次のように任意のタイプに一致します.
    public void test(List<?> c) {
      ...
    }
    

    c中の要素は常にObjectタイプであり、cはListであるため、List実行時にはObjectのみが保存され、汎用型はコンパイル期間中にコード提示作用を行うだけで、実際に実行時にはObjectが格納される.nullは、すべての参照タイプのインスタンスです.

    9.3.2タイプワイルドカードの上限を設定する


    Javaは、制限された汎用ワイルドカードを提供します.
    public void drawAll(List<?> shapes) {
      for(Object obj : shapes) {
        // 
        Shape s = (Shape) obj;
        s.draw();
        ...
      }
    }
    // ? List Shape ( ), 
    public void drawAll(List<? extends Shape> shapes) {
      // , Shape, 
      for(Shape s : shapes) {
        s.draw();
        ...
      }
      shapes.add(0, new Rectangle()); // (1) 
    }
    

    (1)ワイルドカードの上限はShapeだが?表示される実際のタイプが分からないので、オブジェクトを入れることはできません.

    9.4汎用方法


    9.4.1汎用メソッドの定義

    static void fromArrayToCollection(Object[] a, Collection<Object> c) {
      for(Object obj:a) {
        c.add(obj);
      }
    }
    

    以上の方法の限界は、Object配列の要素のみをObjectのCollectionコレクションにコピーでき、Collectionは使用できません.ワイルドカードCollection>を使用しても実行できません.オブジェクトを未知のタイプのコレクション(コレクション内の要素タイプが未知)に配置することはできません.この問題を解決するために、一般的な方法を導入し、文法フォーマットは以下の通りです.
      <T, S>    ( ) {
      // ...
    }
    // :
    static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
      for(T o : a) {
        c.add(o);
      }
    }
    
    public static void main(String[] args) {
      Integer[] ia = new Integer[100];
      Collection<Number> cn = new ArrayList<>();
      fromArrayToCollection(ia, cn);
    }
    

    汎用メソッドが呼び出されると、コンパイラは実パラメータに基づいてタイプパラメータの値を推定し、通常最も直接的なタイプパラメータを推定します.Integer[]iaはT[]aにTのタイプがIntegerであることを知らせる.

    9.4.2汎用メソッドとタイプワイルドカードの違い

  • 多くの場合、ワイルドカード
  • を汎用的な方法で置き換えることができる.
  • ワイルドカードを使用する場合:柔軟なサブクラス化をサポートするために使用される
  • 汎用メソッドは、メソッドの1つ以上のパラメータ間のタイプ依存関係を表すためにタイプパラメータを使用したり、メソッドが値とパラメータ間のタイプ依存関係を返したりすることを可能にする.このようなタイプ依存関係がなければ,汎用メソッドを用いるべきではない.
  • パラメータaのタイプまたは戻り値のタイプが別のパラメータbのタイプに依存する場合、bのタイプ宣言はワイルドカードを使用すべきではない.ワイルドカードを使用してタイプbが不確定であることを示すため、aのタイプも確定できない.この場合、汎用メソッドを使用することを考慮する.
  • タイプが依存しない場合は、ワイルドカードを使用します.

  • 9.4.3 Java 7の「菱形」構文と汎用コンストラクタ

  • コンストラクタは汎用
  • を有することができる.
    class Foo<E> {
      public <T> Foo(T t) {
        ...
      }
    
      public static void main(String[] args) {
        new Foo("abc");
        new Foo(3);
        new <String> Foo("abc");
        new <String> Foo(3.2); // (1)  
        Foo<String> foo = new Foo<>(3); //  E ,“abc” T 
        Foo<String> foo = new <Integer> Foo<String>(3); //  T Integer, 
        Foo<String> foo = new <Integer> Foo<>(3); // (2) , 
      }
    }
    

    (1)汎用コンストラクタにおけるTのパラメータはStringタイプであり,実際にはDoubleタイプ(2)汎用コンストラクタにおける汎用タイプがIntegerであることを明示的に指定すると菱形構文を用いてコンストラクタの後<>におけるクラスのEのタイプを省略することはできない.

    9.4.4ワイルドカード下限設定


    ワイルドカードの下限構文:super T>は、親の特定のタイプにかかわらず、子の特定のタイプにのみ関係し、ワイルドカードの下限を使用します.

    9.4.5汎用方法と重荷


    メソッドパラメータの汎用性に応じてメソッドの再ロードを許可しますが、呼び出し時にコンパイラがどのメソッドを呼び出すか分からない場合は、コンパイルエラー、(3)エラーを報告します.
    public class MyUtils {
      // (1)
      public static <T> void copy(Collection<T> dest, Collection<? extends T> src) {...}
      // (2)
      public static <T> T copy(Collection<? super T> dest, Collection<T> src) {...}
      public static void main(String[] args) {
        List<Number> ln = new ArrayList<>();
        List<Integer> li = new ArrayList<>();
        copy(ln, li); // (3)  
      }
    }
    

    9.5消去と変換

  • 汎用型の実際のタイプパラメータを指定しない場合、このタイプパラメータはraw type(元のタイプ)と呼ばれ、デフォルトはパラメータを宣言するときに指定される最初の上限タイプです.
  • List変数がListに付与されると、List集合中の要素のタイプチェックがタイプ変数の上限であるObjectとなる.

  • 9.5汎用と配列

  • Java 5の汎用設計原則は、コードがコンパイル時に「[unchecked]チェックされていない変換」という警告を出さなければ、プログラムが実行時にClassCastExceptionを起こさないことである.言い換えれば、汎用型を使用した後、「[unchecked]」警告タイムズClassCastExceptionが表示されないことはできません.
  • 配列要素タイプには、配列要素がリストであるが、リスト内の要素のタイプを指定できないなど、タイプ変数またはタイプパラメータを含めることはできません.
  • は、ArrayList[]形式の配列のみを宣言し、ArrayList[10]のような配列オブジェクトを定義することはできない.
  • はタイプワイルドカードを使用できますか?のような配列オブジェクトを作成します.ワイルドカードを使用すると、変換時にClassCastExceptionを回避するためにinstanceofタイプチェックを手動で追加する必要があります.これは、[unchecked]警告時報ClassCastExceptionが表示されないためです.