java泛型(擦除)

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中的泛型做了什么。看下面这段代码:

public class GenTest<T> {
    T value;

    
public T getValue() {
        
return value;
    }

    
public void setValue(T t) {
        value 
= t;
    }
}


使用javap命令反编译生成的GenTest类的class文件,可以得到下面的输出:

javap --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."<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 class UseGenTest {

    
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文件,可以得到下面的输出:

D:\mymise\eclipse\workspace\Test\bin>javap --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."<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类型),而对外,所有返回值类型为泛型类型的方法,在真正使用返回值之前,都是会经过类型转换的。

努力奋斗的小墨鱼 ---- http://www.cnblogs.com/WayneZeng/
原文地址:https://www.cnblogs.com/WayneZeng/p/2697233.html