首先看一下Java中的泛型做了什么。看下面这段代码:
public class GenTest
T value; public T getValue() { return value;
} public void setValue(T t) {
value = t;
}
}
使用javap命令反编译生成的GenTest类的class文件,可以得到下面的输出:
javap -c -p GenTest
Compiled from "GenTest.java"public class GenTest extends java.lang.Object{
java.lang.Object value;public GenTest();
Code: 0: aload_0 1: invokespecial #12; //Method java/lang/Object."
4: returnpublic java.lang.Object getValue();
Code: 0: aload_0 1: getfield #23; //Field value:Ljava/lang/Object;
4: areturnpublic void setValue(java.lang.Object);
Code: 0: aload_0 1: aload_1 2: putfield #23; //Field value:Ljava/lang/Object;
5: return}
我们清楚的看到,泛型T在GenTest类中就是Object类型(java.lang.Object value;)。同样,get方法和set方法也都是将泛型T当作Object来处理的。如果我们规定泛型是Numeric类或者其子类,那么在这里泛型T就是被当作Numeric类来处理的。
好,既然GenTest类中没有什么乾坤,那么我们继续看使用GenTest的时候又什么新东西:
public class UseGenTest { public static void main(String[] args) {
String value = "value";
GenTest
test.setValue(value);
String nv = test.getValue();
}
}
使用javap命令反编译生成的GenTest类的class文件,可以得到下面的输出:
D:\mymise\eclipse\workspace\Test\bin>javap -c -p UseGenTest
Compiled from "UseGenTest.java"public class UseGenTest extends java.lang.Object{public UseGenTest();
Code: 0: aload_0 1: invokespecial #8; //Method java/lang/Object."
4: returnpublic static void main(java.lang.String[]);
Code: 0: ldc #16; //String value
2: astore_1 3: new #18; //class GenTest
6: dup 7: invokespecial #20; //Method GenTest."
10: astore_2 11: aload_2 12: aload_1 13: invokevirtual #21; //Method GenTest.setValue:(Ljava/lang/Object;)V
16: aload_2 17: invokevirtual #25; //Method GenTest.getValue:()Ljava/lang/Object;
20: checkcast #29; //class java/lang/String
23: astore_3 24: return}
重点在17、20和23三处。17就是调用getValue方法。而20则是关键——类型检查。也就是说,在调用getValue方法之后,并没有直接把返回值赋值给nv,而是先检查了返回值是否是String类型,换句话说,“String nv = test.getValue();”被编译器变成了“String nv =(String)test.getValue();”。最后,如果检查无误,在23处才会赋值。也就是说,如果没有完成类型检查,则会报出类似ClassCastException,而代码将不会继续向下执行,这就有效的避免了错误的出现。
也就是说:在类的内部,泛型类型就是被基类型代替的(默认是Object类型),而对外,所有返回值类型为泛型类型的方法,在真正使用返回值之前,都是会经过类型转换的。
为什么不支持泛型的数组?
根据上面的分析可以看出来,泛型其实是挺严谨的,说白了就是在“编译的时候通过增加强制类型转换的代码,来避免用户编写出可能引发ClassCastException的代码”。这其实也算是Java引入泛型的一个目的。
但是,一个颇具讽刺意味的问题出现了:如果允许了泛型数组,那么编译器添加的强制类型转换的代码就会有可能是错误的。
看下面的例子:
//下面的代码使用了泛型的数组,是无法通过编译的GenTest
Object[] test = genArr;
GenTest
strBuf.setValue(new StringBuffer());
test[0] = strBuf;
GenTest
上面的代码中,最后一行是重点。根据本文第一部分的介绍,“String value = ref.getValue()”会被替换成“String value =(String)ref.getValue()”。当然我们知道,ref实际上是指向一个存储着StringBuffer对象的GenTest对象。所以,编译器生成出来的代码是隐含着错误的,在运的时候就会抛出ClassCastException。
但是,如果没有“String value = ref.getValue();”这行代码,那么程序可以说没有任何错误。这全都是Java中多态的功劳。我们来分析一下,对于上面代码中创建出来的GenTest对象,其实无论value引用实际指向的是什么对象,对于类中的代码来说都是没有任何影响的——因为在GenTest类中,这个对象仅仅会被当作是基类型的对象(在这里也就是Object的对象)来使用。所以,无论是String的对象,还是StringBuffer的对象,都不可能引发任何问题。举例来说,如果调用valued的hashcode方法,那么,如果value指向的是String的对象,实际执行的就是String类中的hashcode方法,如果是StringBuffer的对象,那么实际执行的就是StringBuffer类中的hashcode方法。
从这里可以看出,即使支持泛型数组也不会带来什么灾难性的后果,最多就是可能引发ClassCastException。而且平心而论,这个还是程序员自己的错误,实在算不得是Java编译器的错误。
感觉没必要吧,数组本身就只支持一种数据类型啊