JAva汎用学習ノート(一)

19776 ワード

汎用型
JDKは1.5で汎型を増加させた.汎用型がない前にオブジェクトを「投げ込む」集合では,集合はオブジェクトのタイプを忘れ,すべてのオブジェクトをobjectとして処理する.コレクションがプログラムから取り出されると、タイプ変換を強制する必要があります.これには2つのデメリットがあります.1、強制型変換のコードはプログラムを肥大化させます.2,ClassCastException異常を起こしやすい.
1.5汎用型を追加した後、集合はオブジェクトのタイプを記憶することができ、追加したオブジェクトが要求を満たしていない場合、コンパイル時にエラーを提示する(コンパイル時にタイプチェックを行った).また、オブジェクトを取り出しても強制タイプ変換をする必要はありません.コードをより丈夫で簡潔にします.
では、いったい何が汎用なのでしょうか.汎用とは、クラスまたはインタフェースがタイプパラメータを定義し、クラスとインタフェースを作成するときにタイプ実パラメータを入力することです.例:
//    E      
public class AClass<E>{
    public void printInfo(E info){
        System.out.println(info);
    }
}
//     String      ,AClass  E      String  
AClass ac = new AClass<String>();
ac.printInfo("ZXY...");

汎型類から子類を派遣する
汎用宣言付きインタフェースまたは親を作成した場合、子クラスを割り当てるときに親がタイプパラメータを使用できない場合は、タイプ実パラメータを明確に指定する必要があります.例:
public class Apple<T>{}
      :
```
public class A extends Apple<T>
```
      :
```
public class A extends Apple<String>
```
      :
```
public class A extends Apple //       ,    Object    
```
          、              ,      、                   。
                    ,               。    :
public class A<T> extends Apple<T>{}
public class Apple<T>{
    private T info;

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

    public void setInfo(T info){
        this.info = info;
    }
    public T getInfo(){
        return info;
    }
}
public class A extends Apple<String>{
    public String getInfo(){
        return "  :"+super.getInfo();
    }
}
public class A extends Apple{
    //         Object    ,      Object
    @overrive
    public Object getInfo(){
        return "  :"+super.getInfo();
    }
}

汎用クラスは存在しません
List strList=new ArrayList()を作成しました.StingタイプのArrayListクラスが作成されました.ArrayListをArrayListの特殊なクラスと見なすことができます.しかし、実際にJDKはArrayListのために作成するものではない.classファイルは、Arrayを新しいタイプとして処理しません.次の例を示します.
    List<String> listStr1 = new ArrayList<String>();
    List<Integer> listInt1 = new ArrayList<Integer>();
    System.out.println(listStr1.getClass().getName()); //=>java.util.ArrayList
    System.out.println(listStr1.getClass() == listInt1.getClass()); //=>true

listStr 1と考えるかもしれません.getClass() == listInt1.getClass()はfalseを出力し、listStr 1とlistInt 1は2つの異なるインスタンスであると考えます.しかしlistStr 1とlistInt 1のクラスはjavaである.util.ArrayList.Javaには汎用クラスが存在しないことを示した.汎用タイプのタイプの実パラメータが何であるかにかかわらず、実行時に常に同じクラス(メモリにメモリを1つ占有)があります.静的メソッド、静的変数、静的初期化でタイプパラメータが使用できない理由も連想されます.次のコードはコンパイル時に異常を引き起こします.
class A1<T>{
    private T info;
    public static T staticInfo; //        
}

汎用クラスが存在しないことが分かった.では、次のコードもコンパイル時にエラーを起こします.
listStr1 instanceof ArrayList<String>

instanceofの左側はオブジェクトで、右側はクラスです.ArrayListはクラスではありません.ListStr 1 instanceof ArrayListはこのように書くと間違いありません.
タイプワイルドカード
このような要件がある場合,1つの方法のパラメータはリストタイプのパラメータであり,メソッド内に出入りするリストタイプの実パラメータを印刷する.実装コードは次のとおりです.
    public void test(List<Object> c){
        for(int i=0;i<c.size();i++){
            System.out.println(c.get(i));
        }
    }

呼び出しコードは次のとおりです.
    public static void main(String [] args){
        Tpf t = new Tpf();
        List<String> listStr = new ArrayList<String>();
        listStr.add("Z");
        t.test(listStr); //          The method test(List<Object>) in the type Tpf is not applicable for the arguments (List<String>)
    }

我々の慣性思考StringはObjectのサブクラスであり,Listインタフェースは汎用宣言付きインタフェースであり,ListはListのサブクラスであるべきである.実はこれは成立しないことに注意してください.
配列と汎用比較の例を見てみましょう.
String [] arrayStr = new String[]{};
Object [] arrayObj = arrayStr;

List<String> listStr = new ArrayList<String>();
List<Object> listObj = listStr;//        

上のコードを観察することで、私たちはどんな結論を出すことができますか?つまりStringはObjectのサブクラスであり、String[]は依然としてObject[]のサブクラスであるが、ListはListのサブクラスではない.
コンパイルエラーを上記で報告したコードをコンパイルしてスムーズに実行するには、さまざまな汎用リストの親が必要です.各種汎用リストの親用リスト
    public void test(List<?> c){
        for(int i=0;i<c.size();i++){
            System.out.println(c.get(i));//get(i)      Object  ,         "Z" String
        }
    }

このワイルドカード付きリストは、すべての汎用リストの親を表すだけで、要素を追加することはできません.次のコードがコンパイル異常を引き起こす
List<String> strList = new ArrayList<String>();
strList.add(A);
List<?> list = strList;
list.add("A"); //         ,  add      E     ?,            。      null,null          

ただし、listで指定したインデックスの要素をget()メソッドで返すことができます.
タイプワイルドカードの上限を設定
リストを使用する場合
public class Test9_3_2 {

    public static void main(String [] args){
        List<Long> listLong = new ArrayList<Long>();
        listLong.add(100L);
        listLong.add(200L);

        Test9_3_2 test = new Test9_3_2();
        test.add(listLong);

        //    String   list   add()  。             
        List<String> listStr =new ArrayList<String>();
        listStr.add("A");
        listStr.add("B");
        test.add(listStr);
    }

    public void add(List<?> listNum){
        for(Object n:listNum){
            Number number = (Number) n; //           
            //   long、int、short     double
            double d = number.doubleValue();
            System.out.println(d);
        }
    }
}

上のコードは,呼び出し時にNumber汎用Listのサブクラスに出入りしたいという意味である.しかし、listStrが誤って転送され、add()のforループが強制タイプ変換される場合があります.汎用的な目的は、タイプ変換を回避することです(強制タイプ変換はタイプ変換異常を引き起こす可能性があります).これにより、私たちのコードは肥大化し、煩わしいように見えます.このときNumber汎用リストの親を表す方法があり,需要を満たすために汎用ワイルドカードの上限を設定できる.例は次のとおりです.
public class Test9_3_2 {

    public static void main(String [] args){
        List<Long> listLong = new ArrayList<Long>();
        listLong.add(100L);
        listLong.add(200L);

        Test9_3_2 test = new Test9_3_2();
        test.add(listLong);

        //    String   list   add()  
        List<String> listStr =new ArrayList<String>();
        listStr.add("A");
        listStr.add("B");
        //test.add(listStr); //         (           ),            
    }

    //List<? extends Number>   Number  List   ,List<Long>、List<Integer>    
    public void add(List<? extends Number> listNum){
        for(Number n:listNum){
            Number number = n; //              
            //   long、int、short     double
            double d = number.doubleValue();
            System.out.println(d);
        }
    }
}

タイプパラメータの上限を設定するには
JAva汎用型では、ワイルドカードを使用するときに上限を設定できるだけでなく、タイプパラメータを定義するときに上限を設定することもでき、クラスに渡されるタイプ実パラメータが上限タイプであるか、上限タイプのサブクラスであるかを示すことができます.例は次のとおりです.
public class Test9_3_3<T extends Number> {

    private T info;
    public static void main(String [] args){
        Test9_3_3<Long> test1 = new Test9_3_3<Long>();
        Test9_3_3<Integer> test2 = new Test9_3_3<Integer>();

        //       String  Number   
        Test9_3_3<String> test3 = new Test9_3_3<String>();
    }

}

汎用メソッドの定義
Java 5は汎用クラス、汎用インタフェースをサポートし、汎用メソッドの定義もサポートします.汎用クラスおよび汎用インタフェースは、クラスおよびインタフェース内でタイプパラメータを使用できます.汎用メソッドは、メソッド内でのみタイプパラメータを使用できます.次に、Object配列のすべての要素をCollectionセットに追加する必要があります.次のコードで実装します.
static void fromArrayToCollection(Oject [] a, Collection c){
    for(Object obj:a){
        c.add(obj);
    }
}

上記の実装方法には何の問題もありません.重要なのはcパラメータです.CollectionはCollectionのサブクラスではないと述べています.そのため、この方法には限界があります.配列の要素がCollectionに追加されるだけで、Collection、Collectionなどの他のタイプのCollectionに追加できません.では、汎用ワイルドカード(Collection)を使ってもいいですか?
    <T,S>          (    ){ //    }

汎用メソッドの構文を一般メソッドと比較すると,通常メソッドよりも修飾子と戻り値タイプの間にタイプパラメータ宣言が多くなっていることは見いだせない.複数のタイプのパラメータはカンマで区切られます.次にfromArrayToCollection()法を改良する.
public class Test9_4_1 {
    public static void main(String [] args){
        String [] aStr = {"A","B"};
        Collection<String> collStr = new ArrayList<String>();
        fromArrayToCollection(aStr, collStr);
        System.out.print(collStr.toString());
    }

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

クラスやインタフェースの汎用パラメータとは異なり、汎用メソッドは表示される入力タイプの実パラメータを必要とせず、コンパイラは入力パラメータに基づいてタイプパラメータを判断することができる.上記の例に示すように、fromArrayToCollection()メソッドを呼び出すときにStringタイプに出入りする必要はなく、コンパイラは、入力されたパラメータに基づいてタイプ実パラメータがStringタイプであると推定することができる.コンパイラが汎用メソッドのパラメータタイプであることを正確に推定するために、迷わないでください.もしあなたが迷いを作ったら、コンパイラはコンパイルを通過させません.次のようになります.
    public static void main(String [] args){
        Object [] aStr = {"A","B"};
        Collection<String> collStr = new ArrayList<String>();
        fromArrayToCollection(aStr, collStr); //        
        System.out.print(collStr.toString());
    }

上の例では、配列実パラメータのタイプはCollection実パラメータの汎用タイプと同じである必要があります.そうでなければコンパイラはfromArrayToCollection(T[]a,Collection c)メソッドのTがObjectを表すかStringを表すかを判断できない.
汎用メソッドとタイプワイルドカードの違い
ほとんどの場合、タイプワイルドカードの代わりに汎用メソッドを使用できます.たとえば、javaのCollectionインタフェースの2つのメソッドの定義:
public interface Collection<E>{ boolean containsAll(Collection<?> c); boolean addAll(Collection<? extends E> c); }

上記の2つの方法は、以下のように汎用的な方法で代替することもできる.
public interface Collection<E>{
    boolean containsAll(Collection<T> c);
    <T extends E> boolean addAll(Collection<T> c);
}

上記の方法で用いた形式は,タイプパラメータTに上限を設定し,TタイプはEタイプのサブクラスでなければならず,インタフェース内部ではEが一般的なタイプとして扱うことができる.汎用メソッドでは、タイプパラメータを1つ以上のパラメータタイプパラメータ間の依存関係を表すために使用したり、パラメータタイプパラメータとの値の依存関係を返したりすることができます.このような依存関係がなければ汎型法を用いる必要はなく,もしあれば汎型法を用いることができる.たとえば、次のコードは、この依存関係を示しています.
public class Test9_4_1 {
    public static void main(String [] args){
        List<String> listStr = new ArrayList<String>();
        listStr.add("A");
        listStr.add("B");

        String [] reStr = toArray(listStr, new String[listStr.size()]);
        System.out.println(Arrays.toString(reStr));
    }

    //                   
    public static <T> T[] toArray(List<T> c, T[] array){
        for(int i=0;i<c.size();i++){
            array[i] = c.get(i);
        }
        return array;
    }
}

タイプワイルドカードと汎用メソッド(メソッドに宣言されたタイプパラメータを表示する、メソッド修飾子と戻り値の間の<>)には、メソッド署名でタイプパラメータを定義したり、変数のタイプを定義したりすることができます.ただし、汎用メソッドのタイプパラメータは、対応するメソッドに宣言を表示する必要があります.『=よくわからない