作用
泛型(generics) 是在JDK 5中引入的新特性,它的本质其实是类型参数化。
泛型可以在编译时对类型进行安全检测(如果元素的类型与要求的不一致,在编译阶段就会检测出有错误,不需要进入到运行阶段才能发现类型不一致)。
泛型保证了类型的安全性(在获取集合中的元素时,也不需要进行强制类型转换,程序会自动进行隐式转换,这就保证了数据的安全性),也提高了代码的执行效率。
泛型作为“代码模板”,实现了 一套代码对各种类型的套用, 提高了代码的可重用性。
使用场景
基于泛型的这些特性和作用,可以用在如下场景中:
- 泛型集合:在各种集合中使用泛型,保证集合中元素的类型安全;
- 泛型方法:在各种方法中使用泛型,保证方法中参数的类型安全;
- 泛型类:在类的定义时使用泛型,为某些变量和方法定义通用的类型;
- 泛型接口:在接口定义时使用泛型,为某些常量和方法定义通用的类型;
- 泛型加反射:泛型也可以结合反射技术,实现在运行时获取传入的实际参数等功能。
但是我们要注意,无论我们在哪个地方使用泛型,泛型都不能是基本类型。
泛型标记符
泛型的名称其实是可以随意写的,但我们在实际开发时,为了尽量做到见名知意,还是要尽量采用有意义的名称,通常会使用如下几个常用字母:
- E - Element (在集合中使用,因为集合中存放的是元素)
- T - Type(Java 类)
- K - Key(键)
- V - Value(值)
- N - Number(数值类型)
- ? - 表示不确定的 java 类型
逆变与协变
型变: 分为逆变和协变, 与不变对应, 用来描述类型转换后的继承关系;
- 协变(extends)
例如:Crate<Orange> 是 Crate<? extends Fruit> 的子类型,上界 Fruit;
协变指定了上界(限定父类,允许往下的子类), 限制调用 set 方法, 允许调用get方法来保证类型的安全。所以返回类型只能用协变(extends)。
- 逆变(super)
例如:Crate<Fruit> 是 Crate<? super Orange> 的子类型,下界 Orange;
逆变限定了下界(限定子类,允许往上的父类), 允许调用 set 方法, 并可以将Object赋值给它。所以
参数类型只能用逆变(super)。
静态泛型方法
静态泛型方法要在返回值之前使用泛型声明此方法为泛型方法:
错误写法:public static BaseResponse<T> success() {}
正确写法:public static <T> BaseResponse<T> success() {}
原理
在java中泛型只是一个占位符,必须在传递类型后才能使用。就泛型类而言,类实例化时才能传递真正的类型参数,由于静态方法的加载先于类的实例化,也就是说类中的泛型还没有传递真正的类型参数时,静态方法就已经加载完成。显然,静态方法不能使用/访问泛型类中的泛型。
这和静态方法不能调用普通方法/访问普通变量类似,都是因为静态申明与非静态申明的生命周期不同。因此如果方法没有声明为泛型方法的话,那么这个方法就不能访问泛型、把泛型当成返回值等涉及到泛型的操作。
因此也不能定义泛型静态变量,因为静态变量在java程序一运行时就已经被载入内存,而此时它的类型无法确定,而开辟空间必须知道类型,两者矛盾。