汎用詳細超詳細

8719 ワード

汎用型
JDK 1から5以降、3つの一般的な新しい特性が導入された:汎用、列挙(enum)、注釈(Annotation).ここで、JDK 1.5では汎用は非常に重要な実装技術であり、プログラムのパラメータ変換問題を解決するのに役立つ.
1.問題の引き出し
座標を記述するプログラムクラスPointを定義する必要があるとします.2つの属性x,yを指定する必要があります.この2つのプロパティの内容は、次のように選択できます.
  • x = 10、y = 20 ;
  • x = 10.1、y = 20.1 ;
  • x=東経80度、y=北緯20度
  • では、まず解決しなければならない問題は、Pointクラスのx、yの属性タイプの問題です.この場合、int、double、Stringが保存される必要があります.javaでは、すべてのタイプを保存できるタイプは1つしかありません.Object型の例:Pointクラスの定義
    class Point {
        private Object x ;
        private Object y ;
        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;
        }
    }
    

    例:整数座標の設定
    //     
    Point p = new Point() ;
    p.setX(10); //            Object
    p.setY(20);
    //     
    int x = (Integer) p.getX() ; //        Integer      
    int y = (Integer) p.getY() ;
    System.out.println("x = " +x+",y = "+y);
    

    例:文字列の設定
    //     
    Point p = new Point() ;
    p.setX("  80 ");
    p.setY("  20 ");
    //     
    String x = (String) p.getX() ;
    String y = (String) p.getY() ;
    System.out.println("x = " +x+",y = "+y);
    

    以上のコードは問題を解決したように見えますが、問題を解決する鍵はObjectにあり、問題はObjectにも現れます.例:オブザーバの問題
    //     
    Point p = new Point() ;
    p.setX(10.2);
    p.setY("  20 ");
    //     
    String x = (String) p.getX() ;
    String y = (String) p.getY() ;
    System.out.println("x = " +x+",y = "+y);
    

    このとき設定側のエラーで座標内容がdoubleとStringに設定されていましたが、受信側がわからず、実行時にClassCastExceptionが発生します.ClassCastExceptionとは、関係のない2つのオブジェクトが強転して現れる異常のことです.このとき文法は何の制限もしませんが、実行中にプログラムエラーが発生したため、下への転換は安全ではない操作であり、危険をもたらすと結論しました.
    2.基本使用
    汎用とは、クラス定義時にクラス内の属性やメソッド内のパラメータの具体的なタイプを設定するのではなく、クラス使用時に定義することを意味します.このような汎用的な操作を行うには、タイプタグの宣言が必要です.汎用クラスの基本構文:
    class MyClass {
        T value1;
    }
    

    括弧<>のTはタイプパラメータと呼ばれ、任意のタイプを指す.実際にはこのTは任意に書くことができますが、仕様の目的でJavaはタイプパラメータを表すために単一の大文字を使用することをお勧めします.一般的な例:
  • Tは一般的なクラスを表します.
  • EはElementの意味、またはException異常の意味を表します.
  • KはKeyの意味Vは
  • を表す
  • Valueという意味で、通常Kと併用されています.

  • クラスが形式的に定義されている場合は、汎用クラスと呼ばれます.注意:汎用型はクラスしか受け入れられません.すべての基本データ型はパッケージクラスを使用する必要があります.ここで、汎用クラスは、以下に示すように、複数のタイプパラメータを受信することができることに注意してください.
    lass MyClass {
        T value1;
        E value2;
    }
    public class Test {
        public static void main(String[] args) {
            MyClass myClass1 = new MyClass();
        }
    }
    

    例:Pointクラスの定義
    class Point  { // T    ,        ;               
        private T x ;
        private T y ;
        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 class Test{
        public static void main(String[] args) {
            //     
            Point p = new Point() ; // JDK1.5   
            p.setX("  80 ");
            p.setY("  20 ");
            //     
            String x = p.getX() ; //        
            String y = p.getY() ;
            System.out.println("x = " +x+",y = "+y);
        }
    }
    

    開発されたプログラムがダウングレードを避けることができると、セキュリティ上の危険性が解消されることを意味します.汎用的な出現は、下への転換の需要を徹底的に変えた.汎用型を導入した後、タイプが明確に設定されている場合は、設定タイプである.タイプが設定されていない場合、デフォルトはObjectタイプです.
    3.汎用方法
    汎用型はクラスを定義するだけでなく、メソッドを個別に定義することもできます.例:汎用メソッド定義
    class MyClass{
        public  void testMethod(T t) {
            System.out.println(t);
        }
    }
    

    汎用メソッドが汎用クラスと少し異なる点は,タイプパラメータ,すなわちカッコの部分が戻り値の前に書かれていることである.のTはタイプパラメータと呼ばれ、メソッドのTはパラメータ化タイプと呼ばれ、実行時の本当のパラメータではありません.もちろん、宣言されたタイプパラメータは、戻り値のタイプとしても使用できます.例:タイプパラメータを使用して値を返す汎用メソッド
    class MyClass{
        public  T testMethod(T t) {
            return t;
        }
    }
    

    汎用メソッドは、次のように汎用クラスと共存できます.
    例:汎用メソッドと汎用クラスの共存
    class MyClass{
        public void testMethod1(T t) {
            System.out.println(t);
        }
        public  T testMethod2(T t) {
            return t;
        }
    }
    public class Test {
        public static void main(String[] args) {
            MyClass myClass = new MyClass<>();
            myClass.testMethod1("hello    ");
            Integer i = myClass.testMethod2(100);
            System.out.println(i);
        }
    }
    

    上記のコードでは、MyClassは汎用クラスであり、testMethod 1は汎用クラスの一般的な方法であり、testMethod 2は汎用メソッドである.汎用クラスのタイプパラメータは、汎用メソッドのタイプパラメータと対応していません.汎用メソッドは常に自分で定義したタイプパラメータに準拠しています.汎用クラスの実際のタイプパラメータはStringであり,汎用メソッドに伝達されるタイプパラメータはIntegerであり,両者はコヒーレントではない.しかし,混同を避けるために,1つの汎用クラスに汎用メソッドが存在する場合,両者のタイプパラメータは同名でないことが望ましい.例えば、MyClassコードはこのように変更できます.
    class MyClass{
        public void testMethod1(T t) {
            System.out.println(t);
        }
        public  E testMethod2(E e) {
            return e;
        }
    }
    

    4.ワイルドカード
    プログラムクラスに汎用的な定義を追加すると、ClassCastExceptionの問題は回避されますが、パラメータの統一の問題という新しい状況が発生します.コードを見てください.
    class Message {
        private T message ;
        public T getMessage() {
            return message;
        }
        public void setMessage(T message) {
            this.message = message;
        }
    }
    public class Test {
        public static void main(String[] args) {
            Message message = new Message() ;
            message.setMessage("Hello World");
            fun(message);
        }
        public static void fun(Message temp){
            System.out.println(temp.getMessage());
        }
    }
    

    以上のプログラムは新しい問題をもたらし、現在汎用的なタイプがStringではなくIntegerに設定されている場合.
    public class Test {
        public static void main(String[] args) {
            Message message = new Message() ;
            message.setMessage(99);
            fun(message); //     ,    String
        }
        public static void fun(Message temp){
            System.out.println(temp.getMessage());
        }
    }
    

    必要なソリューション:すべての汎用タイプを受信できますが、ユーザーが勝手に変更することはできません.この場合はワイルドカード「?」を使う必要があります.処理します.例:ワイルドカードの使用
    public class Test {
        public static void main(String[] args) {
            Message message = new Message() ;
            message.setMessage(55);
            fun(message);
        }
        //        "?"             ,         ,      
        public static void fun(Message> temp){
            //temp.setMessage(100);     !
            System.out.println(temp.getMessage());
        }
    }
    

    「?」に基づいて、2つのサブワイルドカードが生成されました.
  • ? extendsクラス:汎用上限の設定:たとえば:?extends Numberは、Numberまたはそのサブクラスのみを設定できることを示します.例えば、Integer、Doubleなどです.
  • ? superクラス:汎用下限の設定:たとえば:?super Stringは、Stringとその親オブジェクトのみを設定できることを示します.

  • 注意:上限は宣言に使用でき、変更できません.下限はメソッドパラメータのみで、内容を変更できます!
    5.汎用インタフェース
    汎用型はクラスに定義できるほか,インタフェースに定義できる場合を汎用インタフェースと呼ぶ.構文:
    interface IMessage { //          
        public void print(T t) ;
    }
    

    このインタフェースの実装サブクラスには、サブクラス定義時に汎用を継続する2つの方法の例があります.
    interface IMessage { //          
        public void print(T t) ;
    }
    class MessageImpl implements IMessage {
        @Override
        public void print(T t) {
            System.out.println(t);
        }
    }
    public class Test {
        public static void main(String[] args) {
            IMessage msg = new MessageImpl() ;
            msg.print("Hello World");
        }
    }
    

    例:サブクラスがインタフェースを実装するときに具体的なタイプを明確に示す
    interface IMessage { //          
        public void print(T t) ;
    }
    class MessageImpl implements IMessage {
        @Override
        public void print(String t) {
            System.out.println(t);
        }
    }
    public class Test{
        public static void main(String[] args) {
            IMessage msg = new MessageImpl() ;
            msg.print("Hello World");
        }
    }
    

    6.タイプ消去
    汎用はJava 1.5バージョンで導入された概念であり、それ以前は汎用の概念はなかったが、明らかに汎用コードは以前のバージョンのコードとよく互換性がある.これは,汎用情報はコードコンパイル段階にのみ存在し,JVMに入る前に汎用に関する情報は消去され,専門用語はタイプ消去と呼ばれるためである.一般的に言えば、汎用クラスと一般クラスはjava仮想マシン内に特別な場所はありません.
    コードを見てみましょう.
    class MyClass{
        private T message;
        public T getMessage() {
            return message;
        }
        public void setMessage(T message) {
            this.message = message;
        }
        public void testMethod1(T t) {
            System.out.println(t);
        }
    }
    public class Test {
        public static void main(String[] args) {
            MyClass myClass1 = new MyClass<>();
            MyClass myClass2 = new MyClass<>();
            System.out.println(myClass1.getClass() == myClass2.getClass());
        }
    }
    

    印刷結果がtrueになったのは、JVMにおけるMyClassとMyClassの両方がMyClassであるためである.class.