概述

  • 本质是参数化类型
    • 通过泛型指定的类型范围来 限制参数的类型
  • 在编译时可检测非法类型

  • Java中的泛型只在编译阶段有效

    • 编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出

      类型擦除

      在编码时使用泛型加上的类型参数,会在编译时去掉
      在编译后生成的 Java 字节码文件中 不包含泛型中的类型信息。
      例如,编码时定义的 List 和 List 在经过编译后统一为 List,JVM 读取的只是 List。

过程:
1、查找用来替换类型参数的具体类(一般为 Object),如果指定了类型参数的上界,则以该上界做为替换时的具体类;
2、把代码中的类型参数都替换为具体的类。

泛型使用

三种使用方式:泛型类、泛型接口、泛型方法

泛型类

  1. // 此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
  2. // 在实例化泛型类时,必须指定T的具体类型
  3. public class Generic<T>{
  4. // key这个成员变量的类型为T,T的类型由外部指定
  5. private T key;
  6. public Generic(T key) { // 泛型构造方法形参key的类型也为T,T的类型由外部指定
  7. this.key = key;
  8. }
  9. public T getKey(){ // 泛型方法getKey的返回值类型为T,T的类型由外部指定
  10. return key;
  11. }
  12. }

定义的泛型类,
如果传入泛型实参,则会根据传入的泛型实参做相应的类型限制;
如果不传泛型实参,在泛型类中使用的泛型类型可以是任何类型。

注意:

  • 泛型的类型参数只能是类类型,不能是简单类型
  • 不能对确切的泛型类型使用 instanceof 操作,编译时会报错

    泛型接口

    1. //定义一个泛型接口
    2. public interface Generator<T> {
    3. public T next();
    4. }

实现泛型接口类:

  • 不传泛型实参

    1. 复制代码
    2. /**
    3. * 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
    4. * 即:class FruitGenerator<T> implements Generator<T>{
    5. * 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class"
    6. */
    7. class FruitGenerator<T> implements Generator<T>{
    8. @Override
    9. public T next() {
    10. return null;
    11. }
    12. }
  • 传入泛型实参

    1. /**
    2. * 如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型
    3. */
    4. public class FruitGenerator implements Generator<String> {
    5. private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
    6. @Override
    7. public String next() {
    8. Random rand = new Random();
    9. return fruits[rand.nextInt(3)];
    10. }
    11. }

    泛型方法

    泛型类,在实例化类的时候指明泛型的具体类型;
    泛型方法,在调用方法的时候指明泛型的具体类型。

  • 泛型方法必须有泛型的声明 ```java /**

    • 这才是一个真正的泛型方法。
    • 首先在public与返回值之间的必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
    • 这个T可以出现在这个泛型方法的任意位置.
    • 泛型的数量也可以为任意多个 */ public K showKeyName(Generic container){ … }

// 这不是一个泛型方法,这就是一个普通的方法,只是使用了Generic这个泛型类做形参而已。 public void showKeyValue1(Generic obj){ Log.d(“泛型测试”,”key value is “ + obj.getKey()); }

// 这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符? // ?是一种类型实参,可以看做为Number等所有类的父类 public void showKeyValue2(Generic<?> obj){ Log.d(“泛型测试”,”key value is “ + obj.getKey()); }

  1. - 泛型类中 泛型方法声明的泛型 泛型类声明的泛型 无关
  2. ```java
  3. class GenerateTest<T> {
  4. public void show_1(T t){
  5. System.out.println(t.toString());
  6. }
  7. //在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。
  8. //由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。
  9. public <E> void show_3(E t){
  10. System.out.println(t.toString());
  11. }
  12. //在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。
  13. public <T> void show_2(T t){
  14. System.out.println(t.toString());
  15. }
  16. }
  • 泛型方法中可以使用可变参数

    public <T> void printMsg( T... args){
      for(T t : args){
          Log.d("泛型测试","t is " + t);
      }
    }
    
  • 静态方法的泛型声明只能在方法上,不能使用泛型类定义的泛型

类型通配符

? 表示所有具体的参数类型,例如 List<?> 在逻辑上 是所有 List<具体类型实参>的父类

泛型的上下边界定义

可以为泛型实参进行上下边界的设置

  • 泛型的上下边界设置,必须和声明一起
  • 上限的限定 <? extends T> 必须是 T 的子类
  • 下限的限定 <? super T> 必须是 T 的父类 ```java // 普通方法限制 传入类型必须是 Number 的子类 public void showKeyValue1(Generic<? extends Number> obj){ Log.d(“泛型测试”,”key value is “ + obj.getKey()); }

// 泛型方法限制 上下边界的设置是在声明部分 public T showKeyName(Generic container){ System.out.println(“container key :” + container.getKey()); T test = container.getKey(); return test; }

// 泛型类 public class Generic{ … } ```