泛型(generic):是指参数化类型的能力。可以定义泛型类和泛型方法,随后编译器会用具体的类型来替换它。
使用泛型的主要优点是,能够在编译时而不是在运行时检测出错误。比如:
public interface Comparable { public interface Comparable<T> {
public int compareTo(Object o); public int compareTo(T o);
} }
a)在jdk1.5之前 b)在jdk1.5
这里的<T>表示形式泛型类型,随后可以用一个具体的类型代替。替换泛型类型称为泛型实例化。
Comparable c = new Date(); Comparable<Date> c = new Date();
System.out.println(c.compareTo("color")); System.out.println(c.compareTo("color"));
c)在jdk1.5之前 d)在jdk1.5
c中的代码可以通过编译,但是在运行时会报一个类型转换异常,因为Date不能和一个String做比较。
d中的代码在编译时就会报错,因为传递给compateTo方法的必须是一个Date类型。
1、定义泛型类和接口
public class GenericStack<E> {
private ArrayList<E> list = new ArrayList<E>();
public int getSize() {
return list.size();
}
public E peek() {
return list.get(getSize() - 1);
}
public void push(E o) {
list.add(o);
}
public E pop() {
E o = list.get(getSize() - 1);
list.remove(getSize() - 1);
return o;
}
public boolean isEmpty() {
return list.isEmpty();
}
}
2、泛型方法
public class GenericMethod {
public static <E> void print(E[] array) {
for(E o : array) {
System.out.println(o);
}
}
public static void main(String[] args) {
Integer[] integer = {1, 2, 3, 4};
String[] string = {"a", "b", "c", "d"};
print(integer);
print(string);
}
}
3、泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。
实例:
public class LinkedStack<T> {
private class Node {
T item;
Node next;
Node() {
item = null;
next = null;
}
Node(T item, Node next) {
this.item = item;
this.next = next;
}
boolean end() {
return item == null && next == null;
}
}
private Node top = new Node(); // End sentinel
public void push(T item) {
top = new Node(item, top);
}
public T pop() {
T result = top.item;
if (!top.end())
top = top.next;
return result;
}
public static void main(String[] args) {
LinkedStack<Number> lss1 = new LinkedStack<Number>();
LinkedStack<Integer> lss2 = new LinkedStack<Integer>();
System.out.println(lss1.getClass());
System.out.println(lss2.getClass());
}
}
输出结果:
class generic.LinkedStack
class generic.LinkedStack
true
由此,我们发现,在使用泛型类时,虽然传入了不同的泛型实参,但并没有真正意义上生成不同的类型,传入不同泛型实参的泛型类在内存上只有一个,即还是原来的最基本的类型(本实例中为LinkedStack),当然,在逻辑上我们可以理解成多个不同的泛型类型。
究其原因,在于Java中的泛型这一概念提出的目的,导致其只是作用于代码编译阶段,在编译过程中,对于正确检验泛型结果后,会将泛型的相关信息擦出,也就是说,成功编译过后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。
4、类型通配符
接着上面的示例探讨,从上面的示例我们知道LinkedStack<Number>和LinkedStack<Integer>都是LinkedStack类型,那么在逻辑上LinkedStack<Number>和LinkedStack<Integer>是否可以看成具有父子关系的类呢?
public static void main(String[] args) {
LinkedStack<Number> lss1 = new LinkedStack<Number>();
LinkedStack<Integer> lss2 = new LinkedStack<Integer>();
getDate(lss1);
getDate(lss2);
}
public static void getDate(LinkedStack<Number> data) {
System.out.println(data.getClass());
}
代码中的getDate(lss2);这一行编译错误。The method getDate(LinkedStack<Number>) in the type LinkedStack<T> is not applicable for the arguments (LinkedStack<Integer>)
因此,在逻辑上LinkedStack<Number>不能视为LinkedStack<Integer>的父类。
那么怎么解决这种问题呢?在这里java提供了通配符类型,类型通配符一般用?表示。
public static void main(String[] args) {
LinkedStack<Number> lss1 = new LinkedStack<Number>();
LinkedStack<Integer> lss2 = new LinkedStack<Integer>();
getDate(lss1);
getDate(lss2);
}
public static void getDate(LinkedStack<?> data) {
System.out.println(data.getClass());
}
当用了通配符之后,错误就没有了,代码可以正确运行。
有时候,我们还可能听到类型通配符上限和类型通配符下限。具体有是怎么样的呢?
在上面的例子中,如果需要定义一个功能类似于getData()的方法,但对类型实参又有进一步的限制:只能是Number类及其子类。此时,需要用到类型通配符上限。
public static void main(String[] args) {
LinkedStack<Number> lss1 = new LinkedStack<Number>();
LinkedStack<Integer> lss2 = new LinkedStack<Integer>();
LinkedStack<String> lss3 = new LinkedStack<String>();
getDate(lss1);
getDate(lss2);
getDate(lss3);
}
public static void getDate(LinkedStack<? extends Number> data) {
System.out.println(data.getClass());
}
此时,显然,在代码getDate(lss3);处调用将出现错误提示。
类型通配符上限通过形如LinkedStack<? extends Number>形式定义,相对应的,类型通配符下限为Box<? super Number>形式,其含义与类型通配符上限正好相反,在此不作过多阐述了。
5、消除泛型和对泛型的限制
泛型是通过一种称为类型消除的方法来实现的。编译器使用泛型信息来编译代码,但是随后会消除它。因此,泛型信息在运行时是不可见的。
泛型存在于编译时。一旦编译器确定泛型类型时安全使用的,就会将它转换为原始类型。
示例:
ArrayList<String> list = new ArrayList<String>(); ArrayList list = new ArrayList();
last.add("test"); 转换为: last.add("test");
String str = list.get(0); String str = (String)list.get(0);
public static void getDate(T[] data) { public static void getDate(Object[] data) {
System.out.println(data.length); 转换为: System.out.println(data.length);
} }
对泛型的限制
1.不能使用new E()
E e = new E();这是错误的,因为运行时,E不可用
2.不能使用new E[]
E[] e = new E[2];这是错误的
规避方法
E[] e = (E())new Object[2];
也不能使用泛型类型创建数组
ArrayList<String> list = new ArrayList<String>[10];
规避方法
ArrayList<String> list = (ArrayList<String>)new ArrayList[10];
3.在静态环境下不允许类的参数是泛型类型
泛型类的静态变量,静态方法和初始化语句中引用泛型类型参数是非法的。
public void Test<E> {
public static void m(E 0) { //非法
}
public static E o; //非法
static {
E o;//非法
}
}
4、异常不能是泛型的
泛型类不能继承Throwable。