您的位置:首页 > 其它

从字节码角度分析泛型类,泛型方法,泛型接口的实现机制(类型擦除)

2017-07-23 21:35 567 查看
在java中泛型本质上是个语法糖,跟C++不一样,java根本不会创建泛型类,一切都只不过是编译器通过类型擦除机制实现的障眼法而已,比如

源码:

public
class
ChildTest<T> extends test {
    Ta = (T)
"hello world"
;
 
    public
void
fun(T t){
        System.out.println("ChildTest");
    }
    public ChildTest(){
       
    }
 
    /**
    *
    *@author : zhengrf1
    *@date创建时间:2017年7月20日上午10:47:38

    */
    public
static void
main(String[]
args) {
        // TODO Auto-generated method stub
        ChildTest<String>mytest=
newChildTest<String>();
        mytest.fun();
        System.out.println(mytest.a.getClass());
    }
}

 

字节码:

 

  Last modified 2017-7-23; size 1074 bytes

  MD5 checksum 25a0b1ff964cae6d640af43d89f72d7b

  Compiled from "ChildTest.java"

public class ChildTest<T extends java.lang.Object> extendstest

  minor version: 0

  major version: 52

  flags: ACC_PUBLIC, ACC_SUPER

Constantpool:

   #1 = Class              #2             // ChildTest

   #2 = Utf8               ChildTest

   #3 = Class              #4             // test

   #4 = Utf8               test

   #5 = Utf8               a

   #6 = Utf8               Ljava/lang/Object;

   #7 = Utf8               Signature

   #8 = Utf8               TT;

   #9 = Utf8               fun

  #10 = Utf8               (Ljava/lang/Object;)V

  #11 = Utf8               (TT;)V

  #12 = Utf8               Code

  #13 = Fieldref           #14.#16        // java/lang/System.out:Ljava/io/Print

Stream;

  #14 = Class              #15            // java/lang/System

  #15 = Utf8               java/lang/System

  #16 = NameAndType        #17:#18        // out:Ljava/io/PrintStream;

  #17 = Utf8               out

  #18 = Utf8               Ljava/io/PrintStream;

  #19 = String             #2             // ChildTest

  #20 = Methodref          #21.#23        // java/io/PrintStream.println:(Ljava/

lang/String;)V

  #21 = Class             #22            // java/io/PrintStream

  #22 = Utf8               java/io/PrintStream

  #23 = NameAndType        #24:#25        // println:(Ljava/lang/String;)V

  #24 = Utf8               println

  #25 = Utf8               (Ljava/lang/String;)V

  #26 = Utf8               LineNumberTable

  #27 = Utf8               LocalVariableTable

  #28 = Utf8               this

  #29 = Utf8               LChildTest;

  #30 = Utf8               t

  #31 = Utf8               LocalVariableTypeTable

  #32 = Utf8              LChildTest<TT;>;

  #33 = Utf8               <init>

  #34 = Utf8               ()V

  #35 = Methodref          #3.#36         // test."<init>":()V

  #36 = NameAndType        #33:#34        // "<init>":()V

  #37 = String             #38            // hello world

  #38 = Utf8               hello world

  #39 = Fieldref           #1.#40         // ChildTest.a:Ljava/lang/Object;

  #40 = NameAndType        #5:#6          // a:Ljava/lang/Object;

  #41 = Utf8               main

  #42 = Utf8               ([Ljava/lang/String;)V

  #43 = Methodref          #1.#36         //ChildTest."<init>":()V

  #44 = Methodref          #1.#45         // ChildTest.fun:()V

  #45 = NameAndType        #9:#34         // fun:()V

  #46 = Class              #47            // java/lang/String

  #47 = Utf8               java/lang/String

  #48 = Methodref          #49.#51        // java/lang/Object.getClass:()Ljava/l

ang/Class;

  #49 = Class              #50            // java/lang/Object

  #50 = Utf8               java/lang/Object

  #51 = NameAndType        #52:#53        // getClass:()Ljava/lang/Class;

  #52 = Utf8               getClass

  #53 = Utf8               ()Ljava/lang/Class;

  #54 = Methodref          #21.#55        // java/io/PrintStream.println:(Ljava/

lang/Object;)V

  #55 = NameAndType        #24:#10        // println:(Ljava/lang/Object;)V

  #56 = Utf8               args

  #57 = Utf8               [Ljava/lang/String;

  #58 = Utf8               mytest

  #59 = Utf8               LChildTest<Ljava/lang/String;>;

  #60 = Utf8               SourceFile

  #61 = Utf8               ChildTest.java

  #62 = Utf8              <T:Ljava/lang/Object;>Ltest;

{

  T a;

    descriptor:Ljava/lang/Object;

    flags:

    Signature: #8                           // TT;

 

  public void fun(T);

    descriptor:(Ljava/lang/Object;)V

    flags: ACC_PUBLIC

    Signature: #11                          // (TT;)V

    Code:

      stack=2, locals=2, args_size=2

         0: getstatic     #13                 // Fieldjava/lang/System.out:Ljav

a/io/PrintStream;

         3: ldc           #19                 // String ChildTest

         5: invokevirtual #20                 // Methodjava/io/PrintStream.prin

tln:(Ljava/lang/String;)V

         8: return

      LineNumberTable:

        line 19: 0

        line 20: 8

      LocalVariableTable:

        Start Length  Slot  Name  Signature

            0       9    0  this   LChildTest;

            0       9    1     t   Ljava/lang/Object;

      LocalVariableTypeTable:

        Start Length  Slot  Name  Signature

            0       9    0  this   LChildTest<TT;>;

            0       9    1     t   TT;

 

  public ChildTest();

    descriptor: ()V

    flags: ACC_PUBLIC

    Code:

      stack=2, locals=1, args_size=1

         0: aload_0

         1: invokespecial #35                 // Methodtest."<init>":()V

         4: aload_0

         5: ldc           #37                 // String hello world

         7: putfield      #39                // Fielda:Ljava/lang/Object;

        10: return

      LineNumberTable:

        line 21: 0

        line 16: 4

        line 23: 10

      LocalVariableTable:

        Start Length  Slot  Name  Signature

            0      11    0  this   LChildTest;

      LocalVariableTypeTable:

        Start Length  Slot  Name  Signature

            0      11    0  this   LChildTest<TT;>;

 

  public static void main(java.lang.String[]);

    descriptor: ([Ljava/lang/String;)V

    flags: ACC_PUBLIC, ACC_STATIC

    Code:

      stack=2, locals=2, args_size=1

         0: new           #1                  // class ChildTest

         3: dup

         4: invokespecial #43                 // Method"<init>":()V

         7: astore_1

         8: aload_1

         9: invokevirtual #44                 // Method fun:()V

        12: getstatic     #13                 // Fieldjava/lang/System.out:Ljav

a/io/PrintStream;

        15: aload_1

        16: getfield      #39                 // Field a:Ljava/lang/Object;

        19: checkcast     #46                 // class java/lang/String

        22: invokevirtual#48                 // Methodjava/lang/Object.getClas

s:()Ljava/lang/Class;

        25: invokevirtual #54                 // Methodjava/io/PrintStream.prin

tln:(Ljava/lang/Object;)V

        28: return

      LineNumberTable:

        line 32: 0

        line 33: 8

        line 34: 12

        line 35: 28

      LocalVariableTable:

        Start Length  Slot  Name  Signature

            0      29    0  args   [Ljava/lang/String;

            8      21    1 mytest   LChildTest;

      LocalVariableTypeTable:

        Start Length  Slot  Name  Signature

            8      21    1 mytest  LChildTest<Ljava/lang/String;>;

}

--注意红色部分,可见在编译成字节码时编译器把所有T类型替换成上帝类Object,那么使用时是怎么根据真正传入的类型String调用String类的相关方法的呢?看绿色部分,当我们真正用到T a这个成员时,编译器在编译时会在代码前面加上类型转换检查指令checkcast,把a转化成String类型。然后再调用String类型的相关方法,这种编译器私自添加向下类型转换机制便称为泛型的类型擦除机制。以此完成语法糖的功能。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐