1 引言
***泛型(Generic)***是指参数化类型的能力。可以定义带泛型类型的类或者方 法,随后编译器会用具体的类型来替换它。
2 使用泛型的优点
从jdk1.5开始,Java允许定义泛型类、泛型接口和泛型方法。
####(1).使用泛型类型重新定义java.lang.Comparable接口####
/// Before jdk 1.5
package java.lang;
public interface Comparable{
public int compareTo(Object o);
}
///Jdk 1.5 and afterward
package java.lang;
public interface Comparable<T>{
public int compareTo(T o);
}
####(2).使用泛型可以使错误在编译时就被检测出#### 使用泛型在编译时就可以确定调用或者返回的参数的数据类型,使得错误在编译时就已经检出。
e.g.1
Comparable c = new Date();
System.out.println(c.compareTo("Red")); //Date型无法和String类型比较会产生运行时错误
Comparable<Date> c = new Date();
System.out.println(c.compareTo("Red")); //Date型无法和String类型比较,在编译时就会报错
e.g.2
ArrayList<String> list = new ArrayList<String>();
list.add("String"); //right
list.add(1); //wrong
3 定义泛型类和接口
public class Stack<E> extends Vector<E> {
public Stack() {
}
public E push(E item) {
addElement(item);
return item;
}
public synchronized E pop() {
E obj;
int len = size();
obj = peek();
removeElementAt(len - 1);
return obj;
}
public synchronized E peek() {
int len = size();
if (len == 0)
throw new EmptyStackException();
return elementAt(len - 1);
}
public boolean empty() {
return size() == 0;
}
public synchronized int search(Object o) {
int i = lastIndexOf(o);
if (i >= 0) {
return size() - i;
}
return -1;
}
private static final long serialVersionUID = 1224463164541339165L;
}
T, type(类型),一般用于类的限定
E, Element(元素),一般用于集合中元素的限定实际上这只是个名字而已,
你想用 U、V、W 都是可以的,甚至是 $123 也可以,只要是合法的 Java 名称都可以的。
4 泛型方法
为了定义一个类为泛型类型,需要将泛型类型放在类名之后,例如, Stack<E>
;
为了定义一个方法为泛型类型需要将泛型类型放在方法返回类型之前,例如, <E> void max(E o1, E o2)
;
public class GenericMethodDemo {
public static void main(String[] args) {
Integer[] integers = {1,2,3,4,5,6};
String[] strings = {"This","is","strings"};
GenericMethodDemo.<Integer>print(integers);
GenericMethodDemo.<String>print(strings);
GenericMethodDemo.print(new Object[]{1,2,3,4});
//the following is wrong
//GenericMethodDemo.<Integer>print(strings);
}
public static <E> void print(E[] list){
for(int i = 0; i < list.length; i++){
System.out.print(list[i] + " ");
}
System.out.println();
}
}
受限的泛型:
可以将泛型定义为另一种类型的子类型。这样的泛型称为受限的(bounded)。
<E extends GenericMethodDemo>
, 这样E
指定为GenericMethodDemo的子类 型,此时,函数中传递的传输也应该如此。*非受限泛型<E>
和<E extends Object>
是一样的。
5 原始类型和向后兼容
可以使用泛型类而无须指定具体类型, 使得早期程序可以兼容高版本的jdk,如: Stack s = new Stack();
等同于 Stack<Object> s = new Stack<Object>();
不使用类型参数的泛型类型称为原始类型(Raw Type),使用原始类型是不安 全的,编译时会产生警告
以下程序执行不会产生错误,编译时会产生警告
ArrayList a = new ArrayList();
a.add(1);
a.add("1");
System.out.println(a.get(0)+" "+a.get(1));
6 通配泛型
import java.util.Stack;
public class WildCardDemo {
public static void main(String[] args) {
Stack<Integer> integers = new Stack<Integer>();
integers.push(1);
integers.push(2);
integers.push(3);
print(integers);
}
public static void print(Stack<Number> number ){
while(!number.isEmpty()){
System.out.print(number.pop()+" ");
}
System.out.println();
}
}
上面程序中的print(integers);
将会报错,因为Stack<Integer>
并不是 Stack<Number>
的实例,尽管Integer
是Number
的子类型,但是Stack<Integer>
并不是 Stack<Number>
的子类型。
print
函数中的参数应该修改为Stack<? extends Number>
几种通配类型:
<?>
称为非受限通配,它和<? extends Object>
是一样的<? extends T>
为受限通配,表示T或者T的一个子类型。<? super T>
称为下限通配,表示T或者T的一个未知父类型
对于<?>
和<? extends T>
的使用我们都很容易理解,那么什么时候使用<? super T>
呢?从下面的一个例子可以明白:
import java.util.Stack;
public class WildCardDemo2 {
public static void main(String[] args) {
Stack<Number> sNumber = new Stack<Number>();
Stack<Integer> sInteger = new Stack<Integer>();
sNumber.add(1);
sNumber.add(2);
sInteger.add(3);
sInteger.add(4);
add(sInteger,sNumber);
}
public static <T> void add(Stack<T> s1,Stack<? super T> s2){
while(!s1.isEmpty()){
s2.push(s1.pop());
}
}
}
如果上面程序中的add(Stack<T> s1,Stack<? super T> s2)
方法写成add(Stack<T> s1,Stack<T> s2)
,那么add(sInteger,sNumber);
将会编译错 误。
如果写成add(Stack<T> s1,Stack<? extends T> s2)
,那么 s2.push(s1.pop());
将会编译错误(类型强制转换无法向下)。
7 消除泛型和对泛型的限制
泛型是使用一种叫***类型消除***(type erasure)的方法来实现的。
- 泛型存在与编译时。
一旦编译器确认泛型类型是安全使用的,就会将它转换成原 始类型。
// before compile
ArrayList<String> list = new ArrayList<String>();
list.add("OK");
String state = list.get(0);
//after translating
ArrayList list = new ArrayList();
list.add("OK");
String state = (String)list.get(0);
- 当编译泛型类、接口和方法时,编译器用Object类代替泛型类型。
public static <E> void print(E[] list){
}
///--> after translate replace
public static void print(Object[] list){
}
- 如果一个泛型类型是受限的,那么编译器就会用一个受限类型来替换它。
public static <E extends GeometricObject>
boolean equalArea( E o1,E o2){};
//-->
public static
boolean equalArea(GeometricObject o1, GeometricObject o2){};
- 泛型类是被他的所有实例所共享
ArrayList<String> sList = new ArrayList<String>();
ArrayList<Integer> iList = new ArrayList<Integer>();
sList instanceof ArrayList; // true
iList instanceof ArrayList; // true
几个限制
- 不能使用new E()
- 不能使用new E[]
E[] elem = new E[capacity]; //wrong
E[] elem = (E[]) new Object[capacity]; //right
- 在静态环境static下不允许类的参数是泛型类型
- 异常类不能是泛型的