java泛型是用擦除实现的,即在编译时把类型信息变为object,然后运行是再动态确定类型信息。
1 //简单的泛型 2 class Test<T> 3 { 4 private T e; 5 public void set(T e){ 6 this.e = e; 7 } 8 public void print(){ 9 System.out.println(e); 10 } 11 }
反编译后:
1 class Test 2 { 3 4 Test() 5 { 6 } 7 8 public void set(Object obj) 9 { 10 e = obj; 11 } 12 13 public void print() 14 { 15 System.out.println(e); 16 } 17 18 private Object e; 19 }
可见,编译器只是简单把类型信息T变为了Object,利用这一点我们可用反射机制突破编译器的限制
1 class Main 2 { 3 4 @SuppressWarnings("unchecked") 5 public static void main(String[] args) throws Exception 6 { 7 //这里把类型参数设为String 8 Test<String> t = new Test<String>(); 9 //通过反射把Integer对象赋给成员变量e 10 t.getClass().getMethod("set",Object.class).invoke(t,new Integer(1)); 11 t.print(); 12 } 13 } 14 15 class Test<T> 16 { 17 private T e; 18 public void set(T e){ 19 this.e = e; 20 } 21 public void print(){ 22 System.out.println(e); 23 } 24 }
运行结果:1
Java中的泛型做了什么
首先看一下Java中的泛型做了什么。看下面这段代码:
T value;
public T getValue() {
return value;
}
public void setValue(T t) {
value = t;
}
}
使用javap命令反编译生成的GenTest类的class文件,可以得到下面的输出:
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."<init>":()V
4 : return
public java.lang.Object getValue();
Code:
0 : aload_0
1 : getfield # 23 ; // Field value:Ljava/lang/Object;
4 : areturn
public 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 static void main(String[] args) {
String value = " value " ;
GenTest < String > test = new GenTest < String > ();
test.setValue(value);
String nv = test.getValue();
}
}
使用javap命令反编译生成的GenTest类的class文件,可以得到下面的输出:
Compiled from " UseGenTest.java "
public class UseGenTest extends java.lang.Object{
public UseGenTest();
Code:
0 : aload_0
1 : invokespecial # 8 ; // Method java/lang/Object."<init>":()V
4 : return
public 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."<init>":()V
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类型),而对外,所有返回值类型为泛型类型的方法,在真正使用返回值之前,都是会经过类型转换的。