1. 基本概念
什么是泛型?泛型用来做什么?
在有泛型类之前,程序员必须使用Object编写适用于多种类型的代码,这不仅繁琐,且很不安全(无类型推断来做编译前检测)。
泛型程序设计意味着编写的代码可以对多种不同类型的对象重用。例如,你不希望为收集一组String和File对象分别写两个类。实际上我们使用的ArrayList类就可完成这个工作。它就是一个泛型类。
2. 泛型类
泛型类(generic class)就是有一个或多个类型变量的类。泛型类相当于普通类的工厂。
简单示例:
public class Pair<T> {
private T first;
private T second;
public Pair(T first, T second) {
this.first = first;
this.second = second;
}
SetterAndGetter.....
}
3. 泛型方法
简单例子
注意:类型变量T,放在修饰符(public static等)后面,返回类型前面,并用<>
包裹。
实例1:
public class ArrayAlg {
/**
* @param a 为 varargs 是一个 T[] 数组
*/
public static <T> T getMiddle(T... a){
return a[a.length / 2];
}
public static void main(String[] args) {
String middle = ArrayAlg.getMiddle("join", "Q.", "Public");
System.out.println(middle);
}
/*
* 输出 Q.
*/
}
泛型变量限定
实例2:
类型变量的限定。对类型变量加以约束。
public class ArrayAlg {
/**
* 返回一对值,一个最大值,一个最小值
*
* @param a 参数
*/
public static <T extends Comparable> Pair<T> minMax(T[] a) {
if (a == null || a.length == 0) {
return null;
}
T min = a[0];
T max = a[0];
for (T t : a) {
if (min.compareTo(t) > 0) {
min = t;
}
if (max.compareTo(t) < 0) {
max = t;
}
}
return new Pair<>(min, max);
}
public static void main(String[] args) {
LocalDate[] birthDays = {
LocalDate.of(2906, 12, 9),
LocalDate.of(1815, 12, 9),
LocalDate.of(1903, 12, 9),
LocalDate.of(1966, 12, 9),
LocalDate.of(1906, 12, 9),
LocalDate.of(1222, 12, 9)
};
Pair<LocalDate> mm = ArrayAlg.minMax(birthDays);
System.out.println("min = " + mm.getFirst());
System.out.println("max = " + mm.getSecond());
}
}
输出:
min = 1222-12-09
max = 2906-12-09
4. 泛型代码及虚拟机
类型擦除
无论何时定义一个泛型类型,都会自动提供一个相应的原始类型(raw type)
。这个原始类型的名字就是去掉类型参数后的泛型类型名。类型变量会被擦除
,并将其替换其限定类型(例如:Pair< T extends xxx > )。特别的,对于无限定的变量(例如:Pair
类型擦除后的结果后,结果就是一个普通的类,就好像Java语言中引入泛型之前实现的类一样。
转换泛型表达式
调用一个泛型方法调用时,如果擦除了返回类型(如将 T 擦除为 Object),编译器会插入强制类型转换。
Pair<Employee> buddies = .......;
Employee buddy = buddies.getFirst();
- 对于原始方法
Pair.getFirst
进行调用 - 将类型擦除后返回的
Object
强制转换为Employee
类型
小总结
对于Java泛型的转换,需要记住以下几个事实:
- 虚拟机中没有泛型,只有普通的类和方法。
- 所有的类型参数都会替换为他们的限定类型。
- 为保持类型安全性,必要时会插入强制类型转换。
5. 泛型类型的继承规则(重要)
简而言之,不管类型S和类型T有什么关系,List<S>
和 List<T>
不会有任何关系!
6. 通配符类型(重要)
如同上一节介绍的一样,严格的泛型类型系统的使用是很复杂的,由此Java设计者引入了一个新的概念,通配符。在通配符类型中,允许类型参数发生变化。
注意:?
与T
的使用位置不完全相同!用途更是不同!T
用于定义泛型。?
用于方便泛型使用。而且?
仅用于方法参数处。
通配符的子类限定
例如:
通配符类型 List< ? extends Person>
,表示任何泛型List类型,他的类型类型参数是Person的子类。如List<Student>
、List<Teacher>
。但不能是List<String>
。
通配符的超类型限定
这是通配符附加的能力,也是相较于普通类型变量限定(T extends xxx)强大的地方。
例如:List<? super Student>
、List< ? super T>
。
无限定通配符
无限定的通配符:List<?>
看起来和原始的不加限定的List<T>
没什么区别。List<?>
是一个脆弱的类型。它对于很多简单的操作非常有用。如果方法中并没有用到实际的类型,那么使用?在一定程度可以简化代码。