泛型是指参数化类型的能力,可以定义泛型类型的类、接口或方法,随后编译器会用具体的类型来替换它
使用泛型的主要优点是:能够在编译时而不是在运行时检测错误
package java.lang; public interface Comparable { //JDK1.5之前 public int compareTo(Object o); } package java.lang; public interface Comparable<T> { //JDK1.5之后 public int compareTo(T o); }
新泛型类型在编译时检测到可能的错误:
Comparable c = new Date(); System.out.println(c.compareTo("red")); Comparable<Date> c = new Date(); System.out.println(c.compareTo("red"));
泛型类型必须是引用类型,不能用像int、double或char这样的基本类型来替换泛型类型:
ArrayList<int> intlist = new ArrayList<int>(); //error
必须使用:
ArrayList<Integer> intList = new ArrayList<Integer>();
java会自动打包操作:
ArrayList<Integer> intList = new ArrayList<Integer>(); intList.add(5); //java会自动地将5包装为new Integer(5)
下面看一个泛型类的例子:
public class GenericStack<E> { 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(); } }
向栈中添加元素:
GenericStack<String> stack1 = new GenericStack<String>(); stack1.push("London"); stack1.push("Paris"); stack1.push("Berlin"); GenericStack<Integer> stack2 = new GenericStack<Integer>(); stack2.push(1); stack2.push(2); stack2.push(3);
public static void main(String args[]) { Integer[] integers = {1, 2, 3, 4, 5}; String[] strings = {"London", "Paris", "New York", "Austin"}; print(integers); 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(); }
下面的例子说明了为什么要使用通配泛型:
public class Main { public static void main(String args[]) { GenericStack<Integer> intStack = new GenericStack<Integer>(); intStack.push(1); intStack.push(2); intStack.push(-2); System.out.print("The max number is " + max(intStack)); } public static double max(GenericStack<Number> stack) { double max = stack.pop().doubleValue(); while(!stack.isEmpty()) { double value = stack.pop().doubleValue(); if(value > max) max = value; } return max; } }
标记行会产生编译错误,因为intStack不是GenericStack<Number>的实例,尽管Integer是Number的子类型,但是GenericStack<Integer> 并不是 GenericStack<Number> 的子类型,为了避免这个问题,可以使用通配泛型类型 ,通配泛型类型有三种形式:
非受限通配:? (它和? extends Object是一样的)
受限通配:? extends T (表示T或T的一个未知子类型)
下限通配: ? super T (表示T或T的一个未知父类型)
使用受限通配修改后的代码如下:
public class Main { public static void main(String args[]) { GenericStack<Integer> intStack = new GenericStack<Integer>(); intStack.push(1); intStack.push(2); intStack.push(-2); System.out.print("The max number is " + max(intStack)); } public static double max(GenericStack<? extends Number> stack) { double max = stack.pop().doubleValue(); while(!stack.isEmpty()) { double value = stack.pop().doubleValue(); if(value > max) max = value; } return max; } }
再看下面的一个例子:
public class Main { public static void main(String args[]) { GenericStack<Integer> intStack = new GenericStack<Integer>(); intStack.push(1); intStack.push(2); intStack.push(-2); print(intStack); } public static void print(GenericStack<?> stack) { while(!stack.isEmpty()) { System.out.print(stack.pop() + " "); } } }
什么时候使用下限通配?看下面的例子:
public class Main { public static void main(String args[]) { GenericStack<String> stack1 = new GenericStack<String>(); GenericStack<Object> stack2 = new GenericStack<Object>(); stack2.push("Java"); stack2.push(2); stack1.push("Sun"); add(stack1, stack2); print(stack2); } public static<T> void add(GenericStack<T> stack1, GenericStack<? super T> stack2) { while(!stack1.isEmpty()) { stack2.push(stack1.pop()); } } public static void print(GenericStack<?> stack) { while(!stack.isEmpty()) { System.out.print(stack.pop() + " "); } } }
泛型是一种称为类型消除的方法实现的,泛型信息在运行时是不可用的,这种方法可以使泛型代码向后兼容使用原始类型的遗留代码
泛型存在于编译时。一旦编译器确认泛型类型是安全使用的,就将它转换为原始类型
例如下面的代码:
ArrayList<String> list = new ArrayList<String>(); list.add("Oklahoma"); String state = list.get(0);
被翻译成如下的代码的原始类型:
ArrayList list = new ArrayList(); list.add("Oklahoma"); String state = (String)list.get(0);
使用泛型类型是有一些限制的:
限制1、不能使用new E()
不能使用泛型类型参数创建实例: E object = new E()
限制2、不能使用new E()
不能使用泛型类型参数创建数组,如:E[] elements = new E[capacity];
可以通过创建一个Object类型的数组,然后将它的类型转化为E[]来规避这个限制,如:E[] elements = (E[]) new Object[capacity];
限制3、在静态环境中不允许类的参数是泛型类型
由于泛型类的所有实例都有相同的运行时类,所以泛型类的静态变量和方法是被它的所有实例所共享的,下面的代码非法:
public calss Test<E> { public static Void m(E o1) { } public static E o1; static { E o2; } }
限制4、异常类不能是泛型的