泛型
泛型最早由C++引入,是对类型的抽象。泛型也许是自从jdk1.1以来最大的变化。
为什么使用泛型
泛型可以带来两个有点,第一,类型的安全,不必再为CalssCastException而苦恼,同时不再为不停的显示转换而烦躁。第二,更快速,高效的代码(对JDK1.5来说未必如此)。
泛型类
泛型类实现了对类型的抽象,可以使用类型来做参数,在使用的时候再使用具体的类型参数。例如我们可以定义一个Pair类,该Pair类用来记录两个相同类型的变量,但是随着类型的变化可能有很多种不同的Pair类型或者有一个类型为Object的Pair类。下面我们就不同的情况来详细说明:
/**
* jdk 1.4 object pair
*/
public class Pair {
private Object first;
private Object second;
public Pair(Object first, Object second) {
this.first = first;
this.second = second;
}
public Object getFirst(){
return first;
}
public Object getSecond(){
return second;
}
}
当然也可以使用具体的类型(例如String)来生成具体的Pair类型。
/**
* jdk 1.5 Generic Pair
*/
public class Pair<T>{
private T first;
private T second;
public Pair(T first, T second) {
this.first = first;
this.second = second;
}
public T getFirst() {
return first;
}
public T getSecond() {
return second;
}
}
可以按以下方法使用:
public static void main() {
Pair<String> strPair = new Pair<String>("first", "second");
String first = strPair.getFirst();
String second = strPair.getSecond();
}
相对于Object Pair来说,从Pair中得到的对象已经是定义的类型(String),而不需要在做Cast。
泛型方法
可以在泛型类内部定义泛型方法,也可以在普通类内部定义泛型方法。以下讲述在普通类内部定义泛型方法的过程。
例如我们要从一个数组中取得中间值,然后作为返回值。
/**
* jdk 1.5 Generic Method
*/
public class ArrayAlg {
/** Creates a new instance of ArrayAlg */
public ArrayAlg() {
}
public static <T> T getMiddle(T[] a) {
return a[a.length / 2];
}
public static void main(String[] args) {
String[] strArr = new String[] {"first", "second", "third" };
string strMidVal = ArrayAlg.<String>getMiddle(strArr);
}
}
上述泛型方法的使用可以得到简化,通过getMiddle()的参数我们可以得到类型参数所以上述调用可简化为:
String strMidVal = ArrayAlg.getMiddle(strArr);
限制类型参数
有些时候我们需要对类型参数加上一定的限制,例如如果我们希望我们的Pair类型用来存放Number的子类型,那么我们可以使用如下的方式来做限制:
/**
* jdk 1.5 Generic Pair
*/
public class Pair<T extends Number>{
private T first;
private T second;
public Pair(T first, T second) {
this.first = first;
this.second = second;
}
public T getFirst() {
return first;
}
public T getSecond() {
return second;
}
public static void main() {
Pair<Integer> strPair = new Pair<Integer>(new Integer(0), new Integer(1));
Integer first = strPair.getFirst();
Integer second = strPair.getSecond();
}
}
这时如果你继续使用:
Pair<String> strPair = new Pair< String >(“first”, “second”);
则会报告编译器错误,因为String不是从Number集成而来的。
其实在<T extends Number>中的extends并不仅表示继承关系也可以表示实现关系,例如,使用如下的方式也可以对类型参数加以限制。
public class Pair<T extends Serializable>{
…
}
当需有两个以上的类型限制时,可以使用&来连接限制类型。例如:也可以使用如下的方式对类型参数加以限制:
public class Pair<T extends Serializable & Comparable>{
…
}
但是要求,又多个限制时,所有的限制类型中只能有一个为类,其他必须为参数,且类必须在限制的第一个出现。
泛型代码和JVM
JVM内部并没有泛型的类型,也就是说JVM没有内在的支持泛型代码,但是JDK1.5之前的JVM并不能运行这些JDK1.5编译之后的泛型代码。
每当你定义一个泛型类型时,系统自动为你生成了一个隐藏的原始类型(Raw Type)。这些隐藏的原始类型的名字与去掉类型列表的泛型类型名字相同,所有的的参数类型定义的变量都会被类型参数的第一个限制类型替换,没有类型参数时使用Ojbect替代,例如针对于上述的Pair<T>类型,系统将自动生成如下隐藏的原始类型:
public class Pair
{
private Object first;
private Object second;
public Pair(Object first, Object second)
{
this.first = first;
this.second = second;
}
public Object getFirst() { return first; }
public Object getSecond() { return second; }
}
而泛型类:
class Interval<T extends Serializable & Comparable>{
…
private T value;
}
会自动生成如下隐藏的原始类型:
class Interval {
…
private Serializable value;
}
针对于如下的代码:
Pair<String> strPair = . . .;
String buddy = strPair.getFirst();
编译器自动的添加了cast,将strPair.getFirst()返回的Object转变为String。
泛型代码的四个重点:
l 虚拟机不支持泛型,只支持基本类型和方法。
l 所有的类型参数都回被该类型参数的限制类型取代。
l 合成的桥方法用来提供多态。
l 为了保证类型的安全,Cast被添加到了合适的位置。
泛型的使用的限制
大部分的限制都来源于Java泛型的实现机制(类型擦除)。
基本类型问题:
基本类型不能用来实例化类型参数。
运行时问题:
由于JVM内部没有泛型的支持,所以如下代码有相同的结果:
if (a instanceof Pair<String>) //<String>被忽略
if (a instanceof Pair<T>) //<String>被忽略
同时如下代码<String>也会被被忽略。
Pair<String> p = (Pair<String>) a
当使用instanceof或者cast时,编译器会产生编译警告。
同时还有如下的奇怪现象:
Pair<String> stringPair = . . .;
Pair<Employee> employeePair = . . .;
if (stringPair.getClass() == employeePair.getClass()) // 总是相同
异常问题:
无法Throw或者Catch到泛型的异常。甚至从Exception或者Throwable继承泛型类是非法的。
数组问题:
不允许使用泛型数组。如下的代码会场生编译错误:
Pair<String>[] table = new Pair<String>(10);
泛型实例化的问题:
泛型不能被实例化,如下代码将会产生编译错误:
public Pair() {
first = new T();
second = new T();
}
但是可以使用Class.newInstance或者Array.newInstance来生成泛型实例。
静态上下文:
静态变量不能使用泛型。
静态方法中不能应用类的泛型属性变量。