一、为什么使用泛型
1.可读性好,一看泛型就知道里面装的什么类型的元素
2.泛型只在编译前存在,编译后会将类型擦除并替换成其限定类型,编译后就是一个普通类。泛型可以在编译前规范代码,防止放入一些不合格的对象导致运行期报错,提高程序健壮性。
3.使用泛型让程序更灵活,各类框架就大量使用泛型
个人理解:泛型可以大概分为两部分:定义泛型和使用泛型,大多数时候我们是在使用泛型,也就是用具体类型限制,比如:List《String》,而写一些通用工具或者框架的时候需要自己定义泛型类,泛型方法等提高程序的灵活性。

二、定义简单泛型类
例如:
定义泛型类:
1 | public class Pair<T>{ |
类型变量使用大写形式,且比较短,在java库中,E表示集合的元素类型,K和V分别表示Key和值,T表示任意值,也可以使用邻近的U和S
使用泛型:
1 | Pair<String> |
三、泛型方法
例:
定义泛型方法
1 | public ArrayAlg { |
泛型的类型变量放在修饰符的后面(这里是public static) ,返回值的前面,泛型方法可以定义在可以定义在普通类中,也可以定义在泛型类中
使用泛型方法
1 | String middle = ArrayAlg.<String>getMiddle("aaa","sss","sdsd"); |
上面限制了是String,如果放入一个int类型的参数就会报错,大多数情况下《String》可以省略的,编译器会自己推导出限制类型;1
ArrayAlg.getMiddle("aaa",123,"sdsd");
上面这句代码不会报错,但是编译器推导的限制类型是String 和 Integer公共父类型,如果用String接收返回值会提示:
1 | Incompatible types. Required String but 'getMiddle' was inferred to T: Incompatible types: Serializable & Comparable<? extends Serializable & Comparable<?>> is not convertible to String |
大概意思是可以赋值给Serializable 或 Comparable类型
四、类型变量的限定
- 我们在定义泛型的时候可以限定其范围:
如:1
public static <T extends Comparable> T min(T[] a)...
我们在使用时就只能使用实现了Comparable接口的类型来限制。
这里是接口但依然使用extends关键字,主要是为了表明是父子关系。不管是类还是接口泛型里都是使用extends
- 一个类型变量或通配符可以有多个限定
如:1
T extends Comparable & Serializable
可以根据需要拥有多个接口超类型,但是限定中至多有一个类,而且这个类必须在列表的第一个
1 | T extends class & interface & interface & interface //就像这样的格式,如果有类,放在第一个 |
五、类型擦除
无论何时定义一个泛型类型,都自动提供一个原始类型,原始类型用第一个限定的类型变量来替换
T 是一个无限定的变量,所以会直接用Object替换,如 private T first 编译后变成 private Object first
如果是1
<T extends Comparable & Serializable>
则原始类型会用第一个(即Comparable)代替,private Comparable first;
在使用泛型的时候编译器会自动加入强制类型转换,转换成我们给定的类型
如:1
2Pair<String> b = ...
String ru = b.getName();
类型擦除后getName返回类型会用Object替换T,编译器会插入String强制类型转换
注意:要考虑类型擦除后带来的隐藏隐患
六、约束与局限性
1、不能用基本类型实例化类型参数
1 | 如:Pair<int>不行,只能用Pair<Integer> |
2、运行时类型查询只适用于原始类型
1 | 例如:if(a instanceof Pair<String>) //报错 |
3、不能创建参数化类型的数组
1 | Pair<String>[] table = new Pair<String>[10];//报错,声明不会报错,但初始化就会报错 |
4、不能实例化类型变量
如:new T[]
5、不能构造泛型数组
如:T[]
6、泛型类的静态上下文中类型变量无效
如:private static T abc;//报错
7、不能抛出或捕获泛型类的实例
如:1
2
3
4
5try{
}catch(T e){
}
不过,在异常规范中使用类型变量是允许的
1 | public static <T extends Throwable> void doWork(T t) thows T{//ok |
8、注意泛型的继承
1 | A extends B |
七、通配符类型
能一定程度解决上面泛型不能继承的问题1
Pair<? extends B>
如果调用上面方法的set方法会报错,不能设置值,因为不能确定是什么类型的值,可以get
? extends B 不能设置值,如果是集合(如List)就不能添加值。因为不能到是什么类型的值,但是能获取值,获取的值一定是B的子类,可以用B接收
? super B 不能获取值,能设置/添加值,因为一定是B的子类,不能获取值,如果获取出来用什么接收呢?因为类型不确定。
- ? 不能添加也不能获取,道理同上,但是能加入null,因为对象都能表示为null
八、反射和泛型
未完。。