從字節碼角度分析泛型類,泛型方法,泛型接口的實現機制(類型擦除)

在java中泛型本質上是個語法糖,跟C++不一樣,java根本不會創建泛型類,一切都只不過是編譯器通過類型擦除機制實現的障眼法而已,比如

源碼:

public classChildTest<T> extends test {

    Ta = (T) "hello world";

 

    public void fun(T t){

        System.out.println("ChildTest");

    }

    public ChildTest(){

       

    }

 

    /**

    *

    *@author : zhengrf1

    *@date創建時間:2017720上午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類型的相關方法,這種編譯器私自添加向下類型轉換機制便稱爲泛型的類型擦除機制。以此完成語法糖的功能。

發佈了80 篇原創文章 · 獲贊 57 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章