JAVA泛型


1.概述

泛型:即“参数化类型”。将类型由原来的具体类型参数化,类似于方法中的变量参数,此时类型同样定义为参数形式,只有在调用/运行时才传入具体的类型。

泛型的本质:为了参数化类型,即在不创建新的类型的情况下,通过反省制定的不同类型来控制形参具体显限制的类型,也就是说在使用泛型的过程中,操作的数据类型被指定为某一参数时,改类型可以用在泛型类、泛型接口、泛型方法中。

2.特性

泛型只在编译阶段有效--->由于JVM的泛型类型擦除。

    List l1 = new ArrayList();
    List l2 = new ArrayList();

    /*输出结果为true*/
    System.out.println(l1.getClass() == l2.getClass());

在编译过程中,验证泛型的类型正确性之后,将相关信息擦除。即泛型类型在编译阶段看似多个不同的类型,实际在编译之后的阶段,都是相同的基本类型。

3.泛型的使用

  • 泛型类
  • 泛型接口
  • 泛型方法

3.1 泛型类

通过泛型可以完成对一组类的操作对外开放相同的接口,典型有各种容器类:List、Set、Map等。

泛型类的基本写法:

    class 类名 <泛型标识>{
      /*该处T(泛型标识)由外部制定*/
      private 泛型标识 成员变量名;

      /**
      *@param 自定义
      *@return 返回泛型标识类型的类型 
      */
      public 泛型标识 成员变量名(@param T t){}
    }


泛型标识:可以写为任意标识,常见的如T、E、K、V。
实例化泛型类时,必须制定T的具体类型(包括自定义类),不能使简单类型(如int、double)。

3.2 泛型接口

泛型接口常被用在各种类的生产器中。

泛型接口的基本写法:

    public interface 接口名<泛型标识/*这里用T*/>{
      public T 方法名();
    }

    /**
     *未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
     *如不声明,编译器报错:"Unknown class"
     */
    class ImplGenerator implements Generator{
      @override
      public T next(){
        return null;  
      }
    }

3.3 泛型通配符

如:

    public void showKetValue(Geberic obj){
                Log.d("泛型测试","key value is " + obj.getKey());
    }


类型通配符一般是使用?代替具体的类型实参。和Integer、String一样都是一种实际的类型。

使用场景:可以解决当具体类型不确定的时候,这个通配符就是?;当操作类型时,不需要使用类型的具体功能时,只是用Object类中的功能,那么可以用?通配符来表示未知类型。

3.4 泛型方法

大多数泛型类中的成员方法也是用了泛型,甚至有的泛型类中包含着泛型方法,学习时要注意区分。

泛型类:在实例化类的时候指明泛型的具体类型;泛型方法:在调用方法的时候指明泛型的具体类型。
泛型方法可出现在任意地方和任意场景中使用。

泛型方法的基本写法:

    /**
     *@param t 传入泛型实参
     *@return T 返回类型为T
     *说明:
     *1)只有声明了public和返回类型中间的才是泛型方法
     *2)表示四该方法将使用的泛型类型为T,诸如T、E、K等,该T可出现在泛型方法任意位置
     *3)Class中的T表示为该类的泛型类型
     *4)泛型的数量也可以为任意多个
     *如:
     *public  K showKeyName(Generic container){...}
     */    
     public  genericMethod(Class t) throws InstantiationException,
      IllegalAccessExceotion{
        T instance = t.newInstance();
        return instance;
      }

3.4.1 泛型方法与可变函数

如:

    public  void printMsg(T...args){
      for(T t:args){
        Log.d("Test"," t is " + t);
      }
    }

3.4.2 静态方法与泛型

类中的静态方法使用泛型:静态方法无法访问类上定义的泛型,方法操作的引用数据类型不确定的时候,必须要将泛型定义在该静态方法上

如:

    public class StaticGenerator {
    ....
    /**
     *如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法)
     *即使静态方法要使用泛型类中已经声明过的泛型也不可以。
     *如:public static void show(T t){..},此时编译器会提示错误信息:
          "StaticGenerator cannot be refrenced from static context"
     */
    public static  void show(T t){}
    }

3.4.3 泛型方法总结

泛型方法可独立于类产生变化,基本原则如下:

无论何时,如果能做到,就尽量使用泛型方法,也就是说,如果使用泛型方法将整个类泛型化,那么就应该使用泛型方法。另外,对于一个static的方法,无法访问泛型类型的参数。所以如果要事static方法使用泛型能力,就必须使其成为泛型方法。

4. 泛型的上下边界

  • 添加上边界,即传入的类型实参必须是指定参数或其子类型
    public void showKeyValue(Generic obj){}
  • 添加下边界,即传入的类型实参必须是制定参数或其父类
    public void showKeyValue(Generic obj){}
  • 无边界,即通配符?,参看"3.3泛型通配符"

例子:

    //在泛型方法中添加上下边界限制的时候,必须在权限声明与返回值之间的上添加上下边界,即在泛型声明的时候添加
    //public  T showKeyName(Generic container),编译器会报错:"Unexpected bound"
    public  T showKeyName(Generic container){
      System.out.println("container key :" + container.getKey());
      T test = container.getKey();
      return test;
    }

:泛型的上下边界添加,必须与泛型的声明在一起