java基础-泛型机制

泛型

  • 本质
  • 泛型的本质是为了参数化类型,也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口、方法中
  • 意义
  • 适用于多种数据类型执行相同的代码逻辑(代码的复用)
  • 泛型中的类型在使用时指定,不需要强制类型转换(类型安全)
  • 类型擦除
  • 泛型机制时从JDK1.5才开始加入的,因此为了兼容之前的jdk版本,java泛型的实现采取了“伪泛型”策略,即java在语法上支持泛型,但是在编译阶段会进行“类型擦除”,将所有的泛型都替换为具体的类型
  • 具体原则:
  • 消除类型参数声明,即删除<>及其包围部分
  • 根据类型参数的上下界推断并替换所有的类型参数为原生态类型(若为无限制通配符或没有规定上下限则替换为Object,若规定了上下限则取父类)
  • 为了保证类型安全会在必要时插入强制类型转换代码
  • 自动产生桥接方法,以保证擦除类型后代码仍具有泛型的多态性
  • 泛型的编译期检查(编译前检查)
  • java编译器时通过先检查代码中的泛型类型,然后再进行类型擦除,最后再进行编译。
  • 编译期类型检查是针对引用的(因为(new XXX)是对内存空间的开辟)而无关他真正引用的对象
  • java基础-泛型机制 - 图1
  • 泛型的多态
  • 类型擦除会引发多态的冲突,jvm使用了桥接方法来解决
  • 对于父类含有参数类型的情况,子类可以在父类中传入父类的参数类型,当子类想要重写父类的方法时就可以直接使用传入的参数类型去重写父类的方法,但是由于java使用的是伪泛型机制,也就导致再编译后父类的参数类型将会被擦除,也就导致了被子类重写的方法与子类在字节码上并不是重写的关系(而是重载),解决该问题的方法就是jvm为我们隐式的生成了桥方法(桥方法中调用了我们自己的方法)java基础-泛型机制 - 图2父类java基础-泛型机制 - 图3子类java基础-泛型机制 - 图4
  • 基本类型不能作为泛型类型
  • 因为类型擦除后,最后都会变为原始类型,所有原始类型(包括Object)都不能用来存储基本数据类型,只能存储他们的包装类型。
  • 泛型类型不能被实例化(如果想要实现可以通过反射)java基础-泛型机制 - 图5
  • 这本质上是由于类型擦除机制导致的,因为在编译期会将泛型类型进行擦除,也就无法得知其类型,也就无法得到该类对应的字节码文件。
  • 泛型数组
  • 泛型数组不能采用具体的泛型类型初始化,只能式以通配符的形式,因为具体泛型类型由于类型擦除的原因可能会导致存入任意类型对象,会在取出时发生异常。java基础-泛型机制 - 图6
  • 泛型类中的静态方法和静态变量
  • 因为静态方法、变量并不属于某一个对象,而参数的实例化是在定义对象的时候指定的,所以并不能知道这个泛型参数时何种类型。(注:请不要将和搞混,泛型类中是可以存在的)java基础-泛型机制 - 图7
  • 异常中使用泛型
  • 不能抛出也不能捕获泛型类的对象(会产生歧义),即不能扩展Throwable
  • 不能再catch中使用泛型变量
  • 可以在异常声名中使用类型变量
  • 可以通过反射来获取到泛型的参数类型(getGenericSuperclass())