Java泛型

什么是泛型及泛型的作用?

  泛型,本质是“参数化类型”。也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。在实际使用时指定具体的数据类型。

  泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。

  Java中的泛型,只在编译阶段有效。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。

  Java中泛型在运行期是不可见的,会被擦除为它的上级类型。如果是没有限定的泛型参数类型,就会被替换为Object.   

  泛型的作用

    1、类型安全:编译器会检查类型,泛型中的类型在使用时指定,不需要强制类型转换

    2、提升可读性:从编译阶段就显式地知道反省集合、泛型方法等处理的对象类型是什么

    3、代码复用:泛型合并了同类型的处理代码,使代码复用度变高

Java泛型中的标记符含义: 
  E - Element (在集合中使用,因为集合中存放的是元素)
  T - Type(Java 类)
  K - Key(键)
  V - Value(值)
  N - Number(数值类型)
   - 泛型通配符( 表示不确定的java类型)
  S、U、V - 2nd、3rd、4th types

  

  <T> 的位置十分讲究,必须在类名之后或方法返回值之前。

泛型的使用
泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法

  1、泛型类

    最常见的是各种集合类。

    泛型类的定义:

  // 泛型类
    public class Generator<T>{
        public T var;
    }

  定义的泛型类,不一定要传入泛型类型实参,在使用泛型的时候如果传入泛型实参,则会根据传入的泛型实参做相应的限制,如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型(Object)

  2、泛型接口

  泛型接口的定义:

     // 泛型接口
    public interface Generator<T>{
        T m();
        void onEvent(E event);
    }

    在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型。

  如,泛型接口的实现类:

public class GeneratorImpl implements Generator<String> {
    @Override
    public String m() {
        return null;
    }

    @Override
    public void onEvent(String event) {

    }
}

  3、泛型方法

 public <T> T genericMethod(T t) {
        // do something
    }
 
public <E> void genericMethod2(E e) {
        // do something
    }

  访问修饰符 public 与返回值之间的 <T> 标识非常重要(<T> 中的T可以写为任意标识,如E、K、V等),标识此方法是泛型方法。而像泛型类中使用了泛型的成员方法并不是泛型方法。

  总结:

  泛型方法能使方法独立于类而产生变化(泛型不依赖与类上的泛型(即使类上没有泛型))。静态方法因为无法访问泛型类型的参数,因此静态方法要使用泛型能力,就必须使其称为泛型方法。

  泛型通配符

  类型通配符一般是使用?代替具体的类型参数。只允许在声明的时候使用(声明使用泛型的类型,而不确定具体类型时)

  泛型上下边界

  如:泛型实参只准传入某种类型的父类或子类。

  类型通配符上限通过形如List<? extends Number>来定义,如此定义就是通配符泛型值接受Number及其下层子类类型

  类型通配符下限通过形如 List<? super Number>来定义,表示类型只能接受Number及其三层父类类型,如 Object 类型的实例

运行时如何取出泛型的具体类型?

  1、如果是继承基类而来的泛型,就用 getGenericSuperclass() , 转型为 ParameterizedType 来获得实际类型

// getGenericSuperclass():返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的直接超类的 Type
ParameterizedType parameterizedType = (ParameterizedType) B.class.getGenericSuperclass();  
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();  
for(Type actualTypeArgument: actualTypeArguments) {  
    // actualTypeArgument
}  

  2、如果是实现接口而来的泛型,就用 getGenericInterfaces() , 针对其中的元素转型为 ParameterizedType 来获得实际类型

// getGenericInterfaces():返回表示某些接口的 Type,这些接口由此对象所表示的类或接口直接实现
ParameterizedType parameterizedType = (ParameterizedType) xxx.getClass().getGenericInterfaces()[0];
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
    // actualTypeArgument 
}    

END.