第八章 泛型程序设计

泛型方法

class ArrayAlg{
	public static <T> T getMiddle(T... a){
		return a[a.length / 2]
	}
}

当调用一个泛型方法时,在方法名前的尖括号中放入具体的类型。类型变量放在修饰符的后面,返回类型的前面。

String middle = ArrayAlg.<String>getMiddle("J","k","L")

类型变量的限定

public static <T extends Comparable> T func(T[] a){...}

这个泛型方法规定了 T 必须实现 Comparable (Comparable本身也是一个泛型类型)接口。此方法只能被实现了Comparable接口的类(如String,localDate)的数组调用。否则会编译报错。

一个类型变量或通配符可以有多个限定

T extends Comparable & Serializable

翻译泛型方法

  • 虚拟机中没有泛型,只有普通的类和方法。
  • 所有的类型参数都用他们的限定类型替换
  • 桥方法被合成来保持多态。
  • 为保持类型安全性,必要时插入强制类型转换。

约束与局限性

  • 不能有基本类型实例化类型参数
没有Pair<double>,只有Pair<Double>
  • 运行时类型查询只适用于原始类型
if (a instanceof Pair<String>)  // Error
Pair<String> p = (Pair<String>) a // Warning ---can only test that a is a Pair

同样的道理,getClass总是返回原始类型
Pair<String> stringPair = .....
Pair<Employee> employeePair = ...
if (StringPair.getClass()==employeePair.getClass()) 
结果为true,因为两次调用getClass豆浆返回Pair.class
  • 不能创建参数化类型的数组
    因为对于泛型类型,擦除会使类型参数无效。能够通过数组存储检查,但是会导致一个类型错误。
Pair<String> table = new Pair<String>[10] // Error
  • 不能实例化类型变量
public Pair() {
	first = new T();
	second = new T();
}  // error

类型擦除会将T改变成Object,而且本意不希望调用new Object。

  • 不能构造泛型数组
public static <T extends Comparable> T[] minmax(T[] a){T[] mm = new T[2]}
// Error
当类型擦除时,Object[]引用赋给Comparable[]时,会发生ClassCastException
  • 泛型类的静态上下文中类型变量无效
public class Singleton<T>{
	private static T singleInstance // Error
	public static T getSingleInstance // error
	{
		if (singleInstance == null) {
			return singleInstance
		}
	}
}

类型擦除后,只有一个Singleton类,只包含一个singleInstance域,因此禁止使用带有类型变量的静态域和方法。

  • 不能抛出或捕获泛型类的实例
public class Problem<T> extends Exception {}  // Error
catch中不能使用类型变量。

通配符类型

如果要编写一个答应雇员的方法:

public static void printBuddies(Pair<Employee> p){}
由于Pair<Employee>和Pair<Manager>没有什么联系,所以并不能将Pair<manager>传递
使用通配符类型
public static void printBuddies(Pair<? extends Employee> p){}
  • 无限定通配符
Pair<?> 和 Pair有很大不同
Pair<?>有?get 和 void set方法
get方法返回值只能赋值给一个Object,set方法不能被调用。
两者本质区别是Pair<?> 可以用任意Object对象调用原始Pair类的setObject方法