最近在看Java源码,在Java容器类中所有容器都用到了泛型,然而Java中的泛型实际上是一种伪泛型。记录一下Java中泛型的实现。

用法

所谓泛型,即将类型参数化。看一个简单的例子,下面是JDK1.6中ArrayList的一个初始化方法

public ArrayList<Collection<? extends E> c) {
	elementData = c.toArray();
    size = elementData.length;
    if(elementData.getClass() != Object[].class) 
    	elementData = Arrays.copyOf(elementData, size, Object[].class);
}

在上面这个例子中,方法参数传入的是Collection<? extend E>, 表示的即一个继承类E的一个容器。

泛型方法

public static <E> void printArray(E[] inputArray)
{
	System.out.println("This is genercity method");
}

泛型类

public class Box<T>{
	private T t;
    public T getT(){
    	return t;
    }
    public void setT(T t) {
    	this.t = t;
    }
}

原理

As we all konw, Java中所有的类都继承自Object的类,所有在上面的代码中,将类型参数换成Object是仍然是可以运行的,而Java实现泛型就是通过这种方式实现的,所以叫伪泛型。

  • Java的泛型实际上是相对编译器而言的,在Java生成的字节码文件中是不包含泛型信息的。在编译过程中,Java将正确的泛型的类型信息擦除变为Object,叫做类型擦除 *

用下面代码来证明这个过程

public class Test{
	public static void main(String[] args) {
    	ArrayList<Integer> arrayList1 = new ArrayList<Integer>();
        ArrayList<String> arrayList2 = new ArrayList<String>();
        System.out.prinltn(arrayList1.getClass() == arrayList2.getClass());
    }
}

从输出为true可以判断Java字节码中是没有泛型信息的。

类型擦除

既然泛型可以用Object来代替,那么为什么还要有类型擦除呢。看下面代码。

public class Test{
	public static void main(String[] args) {
		ArrayList<Object> arrayList1 = new ArrayList<Object>();
        ArrayList<Integer> arrayList2 = new ArrayList<Integer>();
        arrayList1.add(1);
        arrayList2.add(1);
        arrayList1.add("1"); //添加一个String
    }
}

对于上面的arrayList1来说,加入你想把这个容器当成一个Integer容器,但是你却并没有向里面添加Integer。上面编译的代码并不会报错,所有等到运行的时候,取出的时候就很可能报ClassCastException异常。 当时对于arrayList2就不会发生这样的错误。

  • Java泛型通常是针对编译器而言的,对于能在编译期间可以解决的错误,不要放在运行的时候去解决。*

类型擦除的局限性

由于种种原因,Java实现的并不是真正意义上的泛型,类型擦除简便的实现了泛型,但是这同样带来一些局限性。

反射

Java字节码中不包含泛型的类型信息,所以反射方面许多写法都不支持。如下

Collection<E>.class //获取泛型的class文件, 不支持
if(T instanceof Pair<Integer>) //不支持,但可以这样写if(T instanceOf Pair<?>)

定义泛型类,方法和接口

  • 不能通过类型创建对象
//非法
T ele = new T();
T[] arr = new T[10];

由于类型擦除,Java只能创建Object对象,无法创建T类型对象

  • 不能用于静态变量和方法
//非法
publc class Singleton<T> {
	private static T instance;
    public synchronized static T getInstance() {
    	if(instance == null) {
       		 
        }
        return instance;
    }
}

由于类型擦除,Singleton只有一份,没办法做到每个Singleton都是单例的。

  • 泛型与数组

Java禁止创建泛型数组。 泛型容器内部使用Object数组,如果要转换泛型容器为对应的数组,需要使用反射。

参考链接 http://blog.csdn.net/lonelyroamer/article/details/7868820 http://www.cnblogs.com/swiftma/p/5882988.html