泛型程序设计
为什么要使用泛型程序设计:
泛型程序设计(generic programming)意味着编写的代码可以对多种不同类型的对象重用。
例如ArrayList类就可以收集任何类的对象。
定义简单泛型类预定泛型方法
泛型类(generic class)就是有一个或多个类型变量的类。
public class Pair<T>
{
private T first;
private T second;
public Pair() { first = null; sencond = null; }
public Pair(T first, T second) {this.first = first; this.second = second; }
public T getFirst() { return first; }
....
public void setSecond(T newValue ) { second = newValue; }
}
泛型方法可以在普通类中定义,也可以在泛型类中定义
class ArrayAlg
{
public static <T> getMiddle(T... a)
{
return a[a.length / 2];
}
}
类型变量的限定
java的继承中,可以根据需要拥有多个接口超类型,但最多有一个限定可以是类。如果有一个类作为限定,它必须是限定列表中的第一个限定。限定类型用“&”隔开,逗号分割类型变量
下面把minmax重写为一个泛型方法。这个方法可以计算泛型数组的最大值和最小值,并返回一个Pair。
package com.day04.pair2;
import com.day04.Pair;
import java.time.*;
/**
* @Author xcf
* @date 2020-12-28
*/
public class PairTest2 {
public static void main(String[] args) {
LocalDate[] birthdays = {
LocalDate.of(1906,12,9), // G. Hopper
LocalDate.of(1815,12,10), // A. Lovelace
LocalDate.of(1903,12,3), // J.von Neumann
LocalDate.of(1910,6,22) // K. Zuse
};
Pair<LocalDate> mm = ArrayAlg.minmax(birthdays);
System.out.println("min = " + mm.getFirst());
System.out.println("max = " + mm.getSecond());
}
}
class ArrayAlg{
/**
* 求T型对象数组的最小值和最大值
* @param a T型对象的数组
* @return 具有最小值和最大值的对,如果a为空,则为null
*/
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 (int i = 0; i < a.length; i++) {
if (min.compareTo(a[i]) > 0 ) min = a[i];
if (max.compareTo(a[i]) < 0 ) max = a[i];
}
return new Pair<>(min,max);
}
}
泛型代码与虚拟机
虚拟机没有泛型类对象——所有对象都属于普通类。
类型擦除
无论何时定义一个泛型类型,都会自动提供一个相应的原始类型(raw type)。这个原始类型的名字就是去掉类型参数或的泛型类型名。类型变量会被擦除(erased),并替换为其限定类型(或者,对于无限定的变量则替换为Object)
转换泛型方法
类型擦除也会出现类型方法中。
对于泛型的转换,需要记住以下几个事实:
- 虚拟机中没有泛型,只有普通的类和方法。
- 所有的类型参数都会替换为它们的限定类型。
- 会合成桥方法来保持多态。
- 为保持类型安全性,必要时会插入强制类型转换。
限定与局限性
- 不能用基本类型实例化类型参数
- 运行时类型查询只适用于原始类型
- 不能创建参数化类型的数组
- Varargs警告( @SuperssWarinings(“unchecked”) )
- 不能实例化类型变量
- 不能构造泛型数组
- 泛型类的静态上下文中类型变量无效
- 不能抛出或铺货泛型类的实例
- 可以取消对检查型异常的检查
- 注意擦除后的冲突
泛型类型的继承规则
首先思考一个问题,现在有一个 Employee 类和一个继承了 Employee 类的 Manager 类,如下所示:
public class Employee { ... }
public class Manager extends Employee { ... }
那么对于一个普通的泛型类 Practical:
public class Practical<T> { ... }
类型 Practical< Manager > 是 Practical< Employee > 的子类吗?答案是否定的,通过下面这段代码可以对此进行验证:
Practical<Manager> manager = new Practical<>();
Practical<Employee> employee = manager; // error
上述代码的第二行,用一个 Practical< Employee > 类型的变量引用了一个 Practical< Manager > 类型的对象,然而编译器显示这样是不合法的。我们知道,如果 B 是 A 的子类的话,是可以用 A 类型的变量引用 B 类型的对象(向上造型)的。显然,这个例子证明了 Practical< Manager > 并不是 Practical< Employee > 的子类。
也许这看起来会很奇怪,但这样设定是出于安全性的考虑,可以避免一些风险的出现。
public class Practical<T> {
ArrayList<T> list;
public Practical() {
list = new ArrayList<>();
}
public void add(T t) {
list.add(t);
}
public T getFirst() {
// return the top ranked;
}
现在我们补全了一部分 Practical 类的内容,然后执行以下操作:
Practical<Manager> manager = new Practical<>();
Practical<Employee> employee = manager; // 假设可以
manager.add(new Manager());
employee.add(new Employee());
Employee firstEmployee = getFirst();
观察上面的代码,就会发现很多不合理之处,首先第一行实例化了一个新的对象之后,此对象中的 list 列表的成员按道理应该是 Manager 类型的,在第三行向 list 中加入了一个 Manager 类型的对象之后,第四行又加入了一个 Employee 类型的对象(???此处 Employee 可是 Manager 的父类)。在此之后还选出了 list 中的第一名(暂且理解为绩效最好的吧),将“经理”和“普通员工”放一起比较显然是不合理的。
当然,Java泛型类型的继承规则保证了这种“离谱”的情况将不会发生!
泛型及通配符方法综合
package com.day04.pair3;
import com.day02.equals.*;
import com.day04.Pair;
/**
* @Author xcf
* @date 2020-12-28
*/
public class PairTest3 {
public static void main(String[] args) {
Manager ceo = new Manager("Gus Greedy",80000,2003,12,15);
Manager cfo = new Manager("Sid Sneaky",60000,2003,12,15);
Pair buddies = new Pair<Manager>(ceo, cfo);
printBuddies(buddies);
ceo.setBonus(1000000);
cfo.setBonus(500000);
Manager[] managers = { ceo, cfo};
Pair result = new Pair<Employee>();
minmaxBonus(managers,result);
System.out.println("first:" + result.getFirst().getClass().getName()
+ ", second:" + result.getSecond().getClass().getName());
}
public static void printBuddies(Pair<? extends Employee> p){
Employee first = p.getFirst();
Employee second = p.getSecond();
System.out.println(first.getName() +" and " + second.getName() + "are buddies." );
}
public static void minmaxBonus(Manager[] a, Pair<? super Manager> result){
if (a.length == 0) return;
Manager min = a[0];
Manager max = a[0];
for (int i = 0; i < a.length; i++) {
if (min.getBonus() > a[i].getBonus()) min = a[i];
if (max.getBonus() < a[i].getBonus()) max = a[i];
}
result.setFirst(min);
result.setSecond(max);
}
public static void maxminBonus(Manager[] a, Pair<? super Manager> result){
minmaxBonus(a,result);
PairAlg.swapHelper(result);// swapHelper捕获通配符类型
}
//不能写 public static <T super manager>. . .
}
class PairAlg{
public static boolean hasNulls(Pair<?> p){
return p.getFirst() == null || p.getSecond() == null;
}
public static void swap(Pair<?> p){
swapHelper(p);
}
public static <T> void swapHelper(Pair<T> p){
T t = p.getFirst();
p.setFirst(p.getSecond());
p.setSecond(t);
}
}