十:Javaの汎用型

9545 ワード

【定義】
一、汎用型の定義は主に以下の2種類がある.
プログラム符号化には、タイプパラメータを含むタイプがあります.すなわち、汎用パラメータはクラスのみを表すことができ、個別のオブジェクトを表すことはできません.(これは現在一般的な定義です)
プログラム符号化にパラメータを含むクラスがあります.そのパラメータは、クラスやオブジェクトなどを表すことができます.(現在ではテンプレートと呼ばれることが多い)
その定義を使用するにかかわらず、汎用パラメータは、実際に汎用を使用する場合に指定する必要があります.
汎用クラスは、クラスをインスタンス化する際に汎用の具体的なタイプを示す.
汎用メソッドは、メソッドを呼び出すときに汎用の具体的なタイプを示すメソッドです.
二、汎用型を使用する目的:
いくつかの強いタイプのプログラム言語は汎用型をサポートし、その主な目的はタイプの安全を強化し、クラスの変換回数を減らすことであるが、いくつかの汎用型をサポートするプログラム言語は一部の目的しか達成できない.
汎用プログラミング(Genericprogramming)は、作成されたコードが多くの異なるタイプのオブジェクトで再利用できることを意味する.
Java言語のタイプシステムの拡張であり、タイプ別にパラメータ化できるクラスの作成をサポートします.タイプパラメータは、メソッドの形式パラメータが実行時に渡される価値のあるプレースホルダであるように、パラメータ化タイプを使用するときに指定されたタイプのプレースホルダと見なすことができます.
【Java汎用のいくつかのタイプコード】
 
一、汎用コードを使用しない
 
3つの属性x,y,zを含むPersonクラスを定義した.定義を開始すると、この3つの属性が何に使われているのか分からないので、Objectタイプとして定義します.しかし,x,y,zにはint,double,Stringタイプがそれぞれ与えられているので,取り出す際にはこの3つのタイプ値を強制的に変換する必要がある.次のコードがあります.
 
   1. Person.java
<span style="font-size:18px;"> public class Person {
     private Object x;
     private Object y;
     private Object z;
      //  Object  。         
     public Object getX() {
         return x;
      }
     public void setX(Object x) {
        this.x = x;
    }
    public Object getY() {
        return y;
    }
    public void setY(Object y) {
        this.y = y;
    }
    public Object getZ() {
        return z;
    }
    public void setZ(Object z) {
        this.z = z;
    }
 }

2. NoGenericTest.java
 

 public class NoGenericTest {
     public static void main(String[]args){
         Person boy=new Person();
         boy.setX(20);
         boy.setY(22.2);
         boy.setZ("  TT");
         //             ,            。
         int x=(Integer)boy.getX();
         double y=(double)boy.getY();
        String z=(String)boy.getZ();
        
        System.out.println(x);
        System.out.println(y);
        System.out.println(z);
    }
}

3.     
 20
 22.2
   TT</span>

二、1つのタイプの変数の汎用的なコードを使用する
 
汎用クラスPersonを定義し、3つのプロパティx,y,zを定義し、テストクラスでプロパティの値を設定し、印刷します.
<span style="font-size:18px;">  1. Person.java
 public class Person<T> {
    private T x;
     private T y;
    private T z;
     public T getX() {
          return x;
      }
     public void setX(T x) {
         this.x = x;
    }
    public T getY() {
        return y;
    }
    public void setY(T y) {
        this.y = y;
    }
    public T getZ() {
        return z;
    }
    public void setZ(T z) {
        this.z = z;
    }
 }

2. GenericTest.java
 
 public class GenericTest {
    public static void main(String[]args){
         Person boy=new Person();
         boy.setX(20);
         boy.setY(22.2);
         boy.setZ("  TT");
         //        
          System.out.println(boy.getX());
         System.out.println(boy.getY());
        System.out.println(boy.getZ());
    }
 }

3.     
20
22.2
  TT
 、             
              Person,      x,y,            ,     ,        ,   。
 
1. Person.java
 
 public class Person<T1,T2> {
     private T1 x;
     private T2 y;
     public T1 getX() {
         return x;
      }
     public void setX(T1 x) {
         this.x = x;
      }
    public T2 getY() {
        return y;
    }
    public void setY(T2 y) {
       this.y = y;
    }
}

2. GenericTest.java
 
public class GenerricTest {
     public static void main(String[] args){
         Person<String,Integer> boy=new Person<String,Integer>();
         boy.setX("  TT");
         boy.setY(20);
         System.out.println(boy.getX());
        System.out.println(boy.getY());
      }
 
 }
 3.     
    TT
  20</span>

四、汎用的な継承を使用する
汎用クラスPersonを定義し、2つの属性x,yを定義し、別の汎用クラスBoyを定義し、属性zを定義し、BoyはPersonクラスを継承し、テストクラスでは属性の値を設定し、印刷します.
 
  
 1. Person.java
 
 public class Person<T1,T2> {
     private T1 x;
     private T2 y;
     public T1 getX() {
         return x;
      }
     public void setX(T1 x) {
         this.x = x;
      }
    public T2 getY() {
        return y;
    }
    public void setY(T2 y) {
        this.y = y;
    }
}

 2. Boy
 
 public class Boy<T1,T2,T3>extendsPerson<T1,T2> {
   private T3 z;
    public T3 getZ() {
        return z;
    }
    public void setZ(T3 z) {
        this.z = z;
    }
 }

3. GenericTest.java
 

 1public class GenericTest {
 2    public static void main(String[] args){
 3        Boy<String,Integer,Double> boy=new Boy<String,Integer,Double>();
 4        boy.setX("  TT");
 5        boy.setY(20);
 6        boy.setZ(200000.22);
 7        
 8        System.out.println(boy.getX());
 9        System.out.println(boy.getY());
10        System.out.println(boy.getZ());
11    }
12 }

 4.    
1   TT
2 20
3 200000.22

五、汎用的なインタフェースの使用
汎用インタフェースPersonを定義し、2つのメソッドを定義し、次に別の汎用クラスBoyを定義し、汎用インタフェースPersonを実現し、属性x,y,zを定義し、テストクラスで属性の値を設定し、印刷します.
<span style="font-size:18px;">1. Person.java

1 public interface Person<T1,T2> {
2    public T1 getX();
3    public T2 getY();
4 }
 2. Boy

 1public class Boy<T1,T2,T3>implements Person<T1,T2> {
 2    private T1 x;
 3    private T2 y;
 4    private T3 z;
 5    public T1 getX() {
 6        return x;
 7     }
 8    public void setX(T1 x) {
 9        this.x = x;
10    }
11    public T2 getY() {
12        return y;
13    }
14    public void setY(T2 y) {
15        this.y = y;
16    }
17    public T3 getZ() {
18        return z;
19    }
20    public void setZ(T3 z) {
21        this.z = z;
22    }
23
24 }

3. GenericTest.java
 
 1public class GenericTest {
 2    public static void main(String[] args){
 3        Boy<String,Integer,Double> boy=newBoy<String,Integer,Double>();
 4        boy.setX("  TT");
 5        boy.setY(20);
 6        boy.setZ(200000.22);
 7        System.out.println(boy.getX());
 8        System.out.println(boy.getY());
 9        System.out.println(boy.getZ());
10    }
11 }

4.     
1   TT
2 20
3 200000.22</span>

六、汎用方法の使用
説明すると、汎用メソッドを定義する場合は、戻り値の前にを付けて、汎用メソッドであることを宣言し、汎用Tを保持してから、汎用Tをメソッドの戻り値として使用することができます.
一般クラスPersonを定義し、次のコードで汎用メソッドを定義します.
<span style="font-size:18px;">1. Person.java

 1public class Person{
 2    public static<T>T getMiddle(T[]a){
 3        return a[a.length/2];
 4     }
 5    public static void main(String [] args){
 6        String[]name={"  TT","  TT1","  TT2"};
 7        String middle=Person.<String>getMiddle(name);
 8        System.out.println(middle);
 9        
10        Integer[]num={20,22,25};
11        Integer middle1=Person.<Integer>getMiddle(num);
12        System.out.println(middle1);
13        
14        Double[]num1={20.0,22.2,25.5};
15        Double middle2=Person.<Double>getMiddle(num1);
16        System.out.println(middle2);
17    }
18 }

2.     

1   TT1
2 22
3 22.2</span>

七、型変数の限定
次のコードでは、メソッドminで変数smallestタイプをTとして定義します.これは、smallestが任意のクラスのオブジェクトであってもよいことを示しています.次のコードではcompareToメソッドを使用する必要がありますが、TにCompareToメソッドが含まれているとは判断できません.したがって、Tを限定し、コードではTにComparableクラスを継承させる必要があります.次のようになります.
<span style="font-size:18px;">public static<T extendsComparable>T min(T[]a)
   1.Person.java
 1public class Person{
 2    public static<T extends Comparable>T min(T[]a){
 3        if(a==null||a.length==0){
 4            return null;
 5        }
 6        T smallest=a[0];
 7        for(int i=1;i<a.length;i++){
 8            if(smallest.compareTo(a[i])>0){
 9                 smallest=a[i];
10             }
11        }
12        return smallest;
13    }
14    public static void main(String [] args){
15        Integer[]num={20,25,30,10};
16        Integer middle=Person.<Integer>min(num);
17        System.out.println(middle);
18    }
19 }

 2.     
 10</span>

【Java汎用理解】
一、タイプ消去
汎用概念を正しく理解する第一の前提は、タイプ消去を理解することである.(type erasure).Javaにおける汎用は基本的にコンパイラという階層で実現される.生成されたJavaバイトコードには汎用中のタイプ情報は含まれていない.汎用を使用する際に付けられたタイプパラメータは、コンパイラによってコンパイル時に取り除かれる.このプロセスをタイプ消去と呼ぶ.コードで定義されたListやListなどのタイプ、コンパイル後はリストになります.JVMが見ているのはListだけであり、汎用的に付加されたタイプ情報はJVMには見えない.Javaコンパイラは、コンパイル時にエラーが発生する可能性のある場所をできるだけ発見しますが、実行時にタイプ変換異常が発生することは避けられません.
多くの汎用的な奇妙な特性は、このタイプの消去の存在に関連しています.
汎用クラスには独自のクラスオブジェクトはありません.例えばListは存在しない.classまたはListである.class、リストのみ.class.
静的変数は、汎用クラスのすべてのインスタンスによって共有されます.MyClassと宣言されたクラスに対して、その中の静的変数にアクセスする方法は依然としてMyClassである.myStaticVar.new MyClassまたはnew MyClassで作成されたオブジェクトは、静的変数を共有します.
汎用タイプパラメータはJava例外処理のcatch文では使用できません.異常処理はJVMの実行時刻に行われるためである.タイプ情報が消去されるため、JVMは2つの例外タイプMyExceptionとMyExceptionを区別できません.JVMの場合、これらはすべてMyExceptionタイプです.例外に対応するcatch文も実行できません.
二、ベストプラクティス
汎用型を使用するときは、いくつかの基本的な原則に従うことができ、一般的な問題を避けることができます.
汎用クラスと元のタイプの混用をコードで回避します.例えば、ListとListは併用すべきではない.これにより、コンパイラの警告と潜在的なランタイム異常が発生します.JDK 5以前に開発されたレガシーコードを利用する必要があり,そうせざるを得ない場合も,可能な限り関連するコードを分離する.
ワイルドカード付き汎用クラスを使用する場合は、ワイルドカードが表すタイプの概念のセットを明確にする必要があります.特定のタイプが不明なため、多くの操作は許可されていません.
汎用クラスは配列と一緒に使用しないほうがいいです.new Listしか作成できません[10]このような配列ではnew List[10]のようなものは作成できない.これは配列の使用能力を制限し,多くの難解な問題をもたらす.したがって,配列のような機能が必要な場合は,集合クラスを用いる.
コンパイラからの警告情報を無視しないでください.