Java 泛型原理

一、概述

Java 泛型是 JDK 5 中引入的一个新特性,其本质是参数化类型,解决不确定具体对象类型的问题。

所操作的数据类型被指定为一个参数(type parameter)这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。对之前写的泛型机制补充。

二、泛型优点

泛型的优点:

  • 在编译的时候检查类型安全
  • 所有的强制转换都是自动和隐式的(避免强转)
  • 提高代码的重用率(代码复用)

三、泛型通配符

定义泛型时,经常碰见T,E,K,V,?等通配符。本质上这些都是通配符,是编码时一种约定俗成的东西。当然,你换个A-Z中另一个字母表示没有关系,但是为了可读性,一般有以下定义:

  • ? 表示不确定的 java 类型
  • T (type) 表示具体的一个java类型
  • K V (key value) 分别代表java键值中的Key Value
  • E (element) 代表Element

为什么需要通配符?

class  Person{}

class  User extends Person{}


 
static int sum1(List<? extends Person> person){ 
   return 0;
}

static int sum2(List<Person> person){ 
   return 0;
}

public static void main(String[] args)
 {

	List<User> user= new ArrayList<>();
	
	sum1(user);
	//报错err
	sum2(user);

}

User[]与Person]是兼容的, List < Person>与 List< User>不兼容的,集合List是不能协变的,会报错, 而 List< Person>与 List<? extends Person> 是可以的,这就是通配符的作用。而泛型上下限为了解决泛型中隐含的转换问题。

通配符通常分三类:

  • 无边界通配符, <?>:没有任何限定。
  • 上边界限定通配符, <? extends E>:上边界限定通配符。
  • 下边界通配符, <? super E>:下边界限定通配符

无界通配符 (<?>)可以适配任何引用类型,看起来与原生类型等价,但是与原生类型还是有区别,使用无界通配符则表明在使用泛型 。同时, List<?>list不可以添加任何类型,因为并不知道实际是哪种类型。但是List list因为持有的是Object类型对象,所以可以add任何类型的对象。

四、泛型擦除

Java泛型是从JDK 1.5才开始加入的,因此为了兼容之前的版本,Java泛型的实现采取了“伪泛型”的策略,即Java在语法上支持泛型,但是在编译阶段会进行所谓的“类型擦除”(Type Erasure),将所有的泛型表示(尖括号中的内容)都替换为具体的类型(其对应的原生态类型),就像完全没有泛型一样。

  • 消除类型参数声明,即删除<>及其包围的部分。
  • 替换所有的类型参数为原生态类型,即根据类型参数的上下界推断,如果类型参数是无限制通配符或没有上下界限定则替换为Object,如果存在上下界限定则替换为类型参数的上界或者下界,比如形如和<? extends Number>的类型参数被替换为Number,<? super Number>被替换为Object。
  • 为了保证类型安全,必要时插入强制类型转换代码
  • 通过“桥接方法”以保证擦除类型后的代码仍然具有泛型的“多态性”

五、泛型的限制与局限

使用Java泛型需要考虑以下一些约束与限制,其实几乎都跟泛型擦除有关。

1、不能用基本类型实例化类型化参数

原因是类型擦除,没有 Pair< double>, 只 有 Pair< Double>。 擦除之后, Pair 类含有 Object 类型的域, 而 Object 不能存储 double值。

2、运行时类型查询只适用于原始类型
3、不能创建参数化类型的数组
4、不能实例化类型变量
5、使用泛型接口时,需要避免重复实现同一个接口
6、定义API返回报文时,尽量使用泛型