***


汎用型の本質はパラメータ化タイプであり、操作されるデータ型がパラメータとして指定される.このタイプのパラメータは、クラス、インタフェース、およびメソッドの作成に使用できます.それぞれ、汎用クラス、汎用インタフェース、汎用メソッドと呼ばれます.
汎用型がない場合、パラメータの変換は、タイプObjectの参照によって実現されます.この変換は、タイプ変換を明示的に強制する必要があります.強制型変換エラーの場合、コンパイラはエラーを提示しないかもしれませんが、実行時には実行できません.これはセキュリティ上の危険です.我々の開発に迷惑をかける可能性があり,実行時のエラーを見つけることが難しく,不要な労力支出を増加させるため,汎用型を用いてパラメトリック型を実現することが一般的に提唱されている.
汎用性のメリット
  • タイプセキュリティ.汎用の主な目的はJavaプログラムのタイプセキュリティを向上させることです.汎用定義を使用する変数のタイプ制限を知ることで、コンパイラはタイプ仮定をより高く検証できます.汎用型はありません.これらの仮定は私たちが自分で覚えたり、コードの注釈をつけたりするしかありません.
  • 強制タイプ変換を削除します.汎用型の1つの利点は、コード内の多くの強制型の変換を除去することです.コードエラー率を低減し、より読みやすくする.
  • 潜在的なパフォーマンス収益.より良い最適化の可能性をもたらすことができます.汎用の初期実装では、コンパイラ強制タイプ変換(汎用がない場合、プログラマはこれらの強制タイプ変換を指定します)が生成されたバイトコードに挿入されます.しかし、より多くのタイプの情報がコンパイラに使用できるという事実は、今後のJVMの最適化に役立ちます.汎用型の実装により、汎用型をサポートするにはJVMやクラスファイルの変更はほとんど必要ありません.すべての作業はコンパイラで完了します.コンパイラが生成したクラスには汎用型(および強制型変換)はありません.ただ、データ型の安全を確保します.

  • 汎用的な使用:
    1.汎用クラス
    汎用タイプはクラスの定義に用いられ、汎用クラスと呼ばれる.汎用型により,クラスのセットに対する操作を対外的に同じインタフェースに開放することができる.最も典型的なのは、リスト、Set、Mapなどの各種容器類である.単純な汎用クラスを定義するには、次のようにします.
    
    //  T          ,    T、E、K、V             
    //        ,    T     
    public class Generics<T>{
          
      //key          T,T          
      private T key;
    
      public Generics(T key) {
          //        key     T,T        
          this.key = key;
      }
    
      public T getKey(){
          //    getKey       T,T        
          return key;
      }
    }
    
    

    注意:汎用型のタイプパラメータは、カスタムクラスを含むクラスタイプのみで、単純なタイプではありません.汎用型のタイプパラメータは、カスタムクラスを含むクラスタイプのみで、単純なタイプではありません.汎用型のタイプパラメータは、カスタムクラスを含むクラスタイプのみで、単純なタイプではありません.入力実パラメトリックタイプは、汎用タイプのパラメータタイプと同じであるIntegerである必要がある.
    public static void main(String args[]) {
         
    	//             (      ),       
    	//                    ,  Integer.
    	Generics<Integer> genericInteger = new Generics<Integer>(123456);
    
    	//                    ,  String.
    	Generics<String> genericString = new Generics<String>("key_vlaue");
    	System.out.println("key is " + genericInteger.getKey());
    	System.out.println("key is " + genericString.getKey());
    
      }
    

    実行結果は次のとおりです.
    key is 123456 key is key_vlaue
    定義された汎用クラスは、必ず汎用タイプの実パラメータに入力しますか?そうではありません.汎用を使用するときに汎用実パラメータが入力されると、入力された汎用実パラメータに基づいて相応の制限が行われ、汎用が本来果たすべき制限の役割を果たすことになります.汎用タイプの実パラメータが入力されない場合、汎用クラスで汎用メソッドまたはメンバー変数を使用して定義されるタイプは、任意のタイプとすることができます.次の例を見てみましょう.
     public static void main(String args[]) {
         
    
    	Generics generic = new Generics("111111");
    	Generics generic1 = new Generics(4444);
    	Generics generic2 = new Generics(55.55);
    	Generics generic3 = new Generics(false);
    	System.out.println("key is " + generic.getKey());
    	System.out.println("key is " + generic1.getKey());
    	System.out.println("key is " + generic2.getKey());
    	System.out.println("key is " + generic3.getKey());
    
      }
    

    実行結果は次のとおりです.
    key is 111111 key is 4444 key is 55.55 key is false
    もう1つは、instanceof操作を正確な汎用タイプに使用できないことです.次の操作が不正である場合、コンパイル時にエラーが発生します.たとえば、次の操作が不正です.
    
    	Generics generic = new Generics("111111");
    	if(generic instanceof Generics<String>) {
         
    		System.out.println("sss");
    	}
    

    ただし、ワイルドカードを使用すればいいです.たとえば、次の操作は和法です.
    Generics generic = new Generics("111111");
    	if(generic instanceof Generics<?>) {
         
    		System.out.println("sss");
    	}
    

    2.汎用インタフェース
    汎用インタフェースは汎用クラスの定義と使用と基本的に同じです.汎用インタフェースは、さまざまなクラスのプロダクションでよく使用されます.一例として、汎用インタフェースを定義します.
    //        
    public interface Generator<T> {
         
        public T next();
    }
    
    

    汎用インタフェースを実装するクラスで、汎用実パラメータが入力されていない場合:汎用実パラメータが入力されていない場合は、汎用クラスの定義と同様に、クラスを宣言する場合は、汎用宣言もクラスに追加する必要があります.
  • すなわちclass FruitGeneratorimplements Generator{}
  • 汎用型を宣言しない場合、たとえばclass FruitGenerator implements Generator、コンパイラは「Unknown class」
  • class FruitGenerator<T> implements Generator<T>{
         
        @Override
        public T next() {
         
            return null;
        }
    }
    

    汎用インタフェースのクラスが実装され、汎用実パラメータが入力された場合:
    汎用インタフェースGeneratorを1つしか作成していませんが、Tのために無数の実パラメータを入力し、無数のタイプのGeneratorインタフェースを形成することができます.クラスが汎用インタフェースを実装する場合、汎用タイプが実パラメータタイプに転送された場合、汎用タイプを使用するすべての場所が転送された実パラメータタイプ、すなわちGenerator、public T next()に置き換えられます.のTは、入力されたStringタイプに置き換えられます.
    public class FruitGenerator implements Generator<String> {
         
        private String[] fruits = new String[]{
         "Apple", "Banana", "Pear"};
        @Override
        public String next() {
         
            Random rand = new Random();
            return fruits[rand.nextInt(3)];
        }
    }
    
    

    3.汎用方法:
    Javaでは汎用クラスの定義は非常に簡単ですが、汎用メソッドは複雑です.特に,我々が見た多くの汎用クラスのメンバメソッドも汎用を用いており,汎用クラスにも汎用メソッドが含まれているものもあり,初心者においては汎用メソッドを誤って理解しやすい.汎用クラスは、クラスをインスタンス化する際に汎用の具体的なタイプを示す.汎用メソッドは、メソッドを呼び出すときに汎用の具体的なタイプを示すメソッドです.
    public <T> T genericMethod(Class<T> tClass)throws InstantiationException ,
      IllegalAccessException{
         
            T instance = tClass.newInstance();
            return instance;
    }
    
    

    汎用的な方法の基本的な紹介
  • @param tClassからの汎用実パラメータ
  • @returnT戻り値はTタイプ
  • 説明:
  • publicと戻り値の中間は非常に重要であり、この方法が汎用的な方法であることを宣言することと理解できる.
  • 宣言されたメソッドのみが汎用メソッドであり、汎用クラスで汎用を使用したメンバーメソッドは汎用メソッドではありません.
  • は、この方法が汎用タイプTを使用することを示しており、この場合に限って、この方法で汎用タイプTを使用することができる.
  • 汎用クラスの定義と同様に、ここでTは任意の識別として任意に書くことができ、T、E、K、Vなどの一般的なパラメータは汎用を表すためによく用いられる.

  • いくつかの例を見て、汎用的な方法の理解を強化します.
    public class GenericTest {
         
       //        ,        
       public class Generic<T>{
              
            private T key;
    
            public Generic(T key) {
         
                this.key = key;
            }
    
            //         ,           ,            。
            //              ,                       。
            //                T     。
            public T getKey(){
         
                return key;
            }
    
            /**
             *            ,                 "cannot reslove symbol E"
             *               E,     E          ,        。
            public E setKey(E key){
                 this.key = keu
            }
            */
        }
    
        /** 
         *             。
         *    public           ,           ,         T
         *   T                .
         *               
         *     :public  K showKeyName(Generic container){
         *        ...
         *        }
         */
        public <T> T showKeyName(Generic<T> container){
         
            System.out.println("container key :" + container.getKey());
            //            ,             。
            T test = container.getKey();
            return test;
        }
         /**
         *          ,             :"UnKnown class 'E' "
         *        ,                      。
         *           T,        E,              E    。
        public  T showKeyName(Generic container){
            ...
        }  
        */
     public <T> T showKeyName(Generic<E> container){
         
            ...
        }  
       /*
         *           ,             :"UnKnown class 'T' "
         *        T            ,                。
         *                  。
         * */
        public void showkey(T genericObj){
         
    
        }
        
    
       
    }
    

    クラス内の汎用メソッド:もちろんこれは汎用メソッドのすべてではありません.汎用メソッドは、任意の場所やシーンで使用することができます.しかし、非常に特殊な場合があります.汎用メソッドが汎用クラスに現れると、もう一つの例を見てみましょう.
    public class GenericFruit {
         
        class Fruit{
         
            @Override
            public String toString() {
         
                return "fruit";
            }
        }
    
        class Apple extends Fruit{
         
            @Override
            public String toString() {
         
                return "apple";
            }
        }
    
        class Person{
         
            @Override
            public String toString() {
         
                return "Person";
            }
        }
    
        class GenerateTest<T>{
         
            public void show_1(T t){
         
                System.out.println(t.toString());
            }
    
            //              ,    E,    E       。     T  ,     。
            //                 ,               ,                    。
            public <E> void show_3(E t){
         
                System.out.println(t.toString());
            }
    
            //              ,    T,    T        ,          T       。
            public <T> void show_2(T t){
         
                System.out.println(t.toString());
            }
        }
    
        public static void main(String[] args) {
         
            Apple apple = new Apple();
            Person person = new Person();
    
            GenerateTest<Fruit> generateTest = new GenerateTest<Fruit>();
            //apple Fruit   ,      
            generateTest.show_1(apple);
            //      ,            Fruit,        Person
            //generateTest.show_1(person);
    
            //            
            generateTest.show_2(apple);
            generateTest.show_2(person);
    
            //             
            generateTest.show_3(apple);
            generateTest.show_3(person);
        }
    }
    
    

    4.汎用の上下境界:
    汎用を使用する場合は、汎用タイプの実パラメータに対して上下境界の制限を行うこともできます.たとえば、タイプの実パラメータは、あるタイプの親クラスまたはあるタイプの子クラスにのみ入力されます.
    例を見てみましょう.汎用に境界を追加します.すなわち、入力されたタイプの実パラメータは、指定されたタイプのサブタイプでなければなりません.
    public void showKeyValue1(Generic<? extends Number> obj){
         
        Log.d("    ","key value is " + obj.getKey());
    }
    
    Generic<String> generic1 = new Generic<String>("11111");
    Generic<Integer> generic2 = new Generic<Integer>(2222);
    Generic<Float> generic3 = new Generic<Float>(2.4f);
    Generic<Double> generic4 = new Generic<Double>(2.56);
    
    //             ,  String     Number     
    //showKeyValue1(generic1); 
    showKeyValue1(generic2);
    showKeyValue1(generic3);
    showKeyValue1(generic4);
    
    

    もう一つの汎用的な方法の例を示します.
    public <T extends Number> T showKeyName(Generic<T> container){
         
        System.out.println("container key :" + container.getKey());
        T test = container.getKey();
        return test;
    }
    
    

    汎用メソッドに上下境界制限を追加する場合は、パーミッション宣言と戻り値の間に上下境界を追加する必要があります.すなわち、汎用宣言時にpublic T showKeyName(Generic container)を追加すると、コンパイラが「Unexpected bound」とエラーを報告します.
    リファレンスリンク