泛型类
public class Pair<T,U> {
private T first;
....
}
上面的T被成为类型变量
泛型字母类型
E 集合的元素类型.
K,V 表的关键字与值的类型
T,U,S 任意类型
泛型类可看作普通类的工厂
泛型方法
class ArrayAlg
{
public static <T> T getMiddle(T... a)
{
return a[a.length/2];
}
}
调用泛型方法时,如何传给泛型方法具体类型
String middle = ArrayAlg.<String>getMiddle("John")
当然可以直接写成如下,编译可以根据传入的参数类型推断出来,而有的编译器可能会报错
String middle = ArrayAlg.getMiddle("John")
类型变量的限定
class ArrayAlg
{
public static <T> T getMiddle(T... a)
{
....
}
}
因为类型变量可以是任何类,我们可以对该类型做一些限定
public static <T extends Comparable> T getMiddle(T... a)
还可以多重限定
public static <T extends Comparable&Serializable> T getMiddle(T... a)
类型变量 擦除
何来擦除
虚拟机是没有泛型类型对象的。
擦除后,用什么来替换泛型
泛型类型都会被原始类型所代替,如果有类
public class Interval<T extends Comparable&Serializable>
有限定类型,那么T被替换为Comparable(原始类型用第一个限定的类型变量来替换)。如果是无限定类型变量T,那么就直接替换为Object。
虚拟机如何处理泛型表达式
原代码:
Pair<Employee> buddies = ...;
Employee buddy = buddies.getFirst();
既然到了虚拟机那里,所有的Employee泛型都变成Object了,getFirst()方法返回的也是Object,那要泛型何用?
其实虚拟机是这样做的:
Pair buddies = ...;
Employee buddy = (Employee)buddies.getFirst();
也就是代码中的泛型最终通过强转类型实现。
兼容泛型出现前的遗留代码所带来的隐性问题
泛型出现前ArrayList无需增加泛型修饰,这带来问题是,虽然18行是由泛型String限制,但是进入方法setList后,10行给list添加了整数1,编译器是检查不到的,只会给出警告
24行读取该list,仍旧不会有问题,直到运行中将整数强转为String 出现异常。
警告中给出的建议是:
* 泛型限定
* 确认不会有问题后,利用@SuppressWarnings ‘unchecked’
运行时的类型
擦除后
Pair<Employee> employeePair;
在虚拟机里和Pair无异了
Pair<String> stringPair = ...
Pair<Employee> employeePair = ...
if(stringPair.getClass() == employeePair.getClass()) //是相同的。
不能创建参数化类型的数组
什么叫 参数化类型的数组呢?不懂,
如下
Pair<String>[] table = new Pair<String>[10];
Object[] objarray = table;
objarray[0] = new Pair<Employee>();//因为类型变量被擦除,编译器不会报错,但这会导致运行中错误。
这种问题是编译器无法检测到的错误是需要力图避免,因为他也是可以被避免的,使用下面来替换数组形式。
ArrayList<Pair<String>> table = ...;
table.add(new Pair<Employee>()); //会编译时报错的。
数组会记住它的元素类型,如下
String[] ss = new String[10];
Object[] os = ss;
os[0] = new Integer(); //会报错
Varargs警告
T.. ts
参数中出现的这个其实是一个数组,那么如果这个T是个带泛型的对象呢?
比如
Pair<Sring>
那么虚拟机就需要为其创立一个带泛型数组,这违背了上面的规矩,但java格外开恩,只需要加@SafeVarargs或@SuppressWarnings ‘unchecked’就可以了。