Java個人對泛型的理解(一)

Java中的泛型<T>

作用: 是將運行時的異常轉到編譯時來處理,代碼更健壯,更簡潔,更靈活,複用性強

AIShoes<T> ; T爲類型參數

 AIShoes<Nike>中的Nike爲實際類型參數

 AIShoes<T>: 這一整個爲泛型類型

 AIShoes<Nike>: 整個稱爲參數化的類型也就是ParameterizedType

用法: 作用在接口之上,比如一個鞋子接口


public interface IShoes<T> {
}

作用在方法之上

    public <T> AIShoes<T> getShoes(){
        return new AIShoes<T>();
    }

作用在類之上

public class AIShoes<T> {
    T t;

    @Override
    public void setShoesType(T t) {
        this.t = t;
    }

    @Override
    public T getShoesType() {
        return t;
    }
}

 

泛型的擦除

原理: 泛型是JDK5引入的,虛擬機其實是不支持泛型的,所以java實現的是一種僞泛型機制. 爲了向下兼容,虛擬機在編譯期就會做擦除泛型的操作,這樣java就不需要產生新的字節碼,所以在java運行時根本就不存在泛型的信息.

泛型是如何擦除的

當一個泛型類編譯成字解碼後,會將這個<T> 給擦除掉

就拿上面的例子來說

public class AIShoes<T> implements IShoes<T> {
    T t;

    @Override
    public void setShoesType(T t) {
        this.t = t;
    }

    @Override
    public T getShoesType() {
        return t;
    }
}

這個類,使用AMS工具查看擦除後是什麼樣的

// class version 51.0 (51)
// access flags 0x21
// signature <T:Ljava/lang/Object;>Ljava/lang/Object;Lcom/ancely/fanxing/demo2/IShoes<TT;>;
// declaration: com/ancely/fanxing/demo2/AIShoes<T> implements com.ancely.fanxing.demo2.IShoes<T>
public class com/ancely/fanxing/demo2/AIShoes implements com/ancely/fanxing/demo2/IShoes  {

  // compiled from: AIShoes.java

  // access flags 0x0
  // signature TT;
  // declaration: T
  Ljava/lang/Object; t

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 3 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lcom/ancely/fanxing/demo2/AIShoes; L0 L1 0
    // signature Lcom/ancely/fanxing/demo2/AIShoes<TT;>;
    // declaration: com.ancely.fanxing.demo2.AIShoes<T>
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x1
  // signature (TT;)V
  // declaration: void setShoesType(T)
  public setShoesType(Ljava/lang/Object;)V
   L0
    LINENUMBER 8 L0
    ALOAD 0
    ALOAD 1
    PUTFIELD com/ancely/fanxing/demo2/AIShoes.t : Ljava/lang/Object;
   L1
    LINENUMBER 9 L1
    RETURN
   L2
    LOCALVARIABLE this Lcom/ancely/fanxing/demo2/AIShoes; L0 L2 0
    // signature Lcom/ancely/fanxing/demo2/AIShoes<TT;>;
    // declaration: com.ancely.fanxing.demo2.AIShoes<T>
    LOCALVARIABLE t Ljava/lang/Object; L0 L2 1
    // signature TT;
    // declaration: T
    MAXSTACK = 2
    MAXLOCALS = 2

  // access flags 0x1
  // signature ()TT;
  // declaration: T getShoesType()
  public getShoesType()Ljava/lang/Object;
   L0
    LINENUMBER 13 L0
    ALOAD 0
    GETFIELD com/ancely/fanxing/demo2/AIShoes.t : Ljava/lang/Object;
    ARETURN
   L1
    LOCALVARIABLE this Lcom/ancely/fanxing/demo2/AIShoes; L0 L1 0
    // signature Lcom/ancely/fanxing/demo2/AIShoes<TT;>;
    // declaration: com.ancely.fanxing.demo2.AIShoes<T>
    MAXSTACK = 1
    MAXLOCALS = 1
}

很明顯,將T 改成了Object

再來看下面的例子,使用的是T extends Shoes>

public class NikeShoes<T extends Shoes>  {
    public void setShoesType(T t) {
        
    }

    public T getShoesType() {
        return null;
    }
}

編譯後

// class version 51.0 (51)
// access flags 0x21
// signature <T:Lcom/ancely/fanxing/demo2/Shoes;>Ljava/lang/Object;
// declaration: com/ancely/fanxing/demo2/NikeShoes<T extends com.ancely.fanxing.demo2.Shoes>
public class com/ancely/fanxing/demo2/NikeShoes {

  // compiled from: NikeShoes.java

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 3 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lcom/ancely/fanxing/demo2/NikeShoes; L0 L1 0
    // signature Lcom/ancely/fanxing/demo2/NikeShoes<TT;>;
    // declaration: com.ancely.fanxing.demo2.NikeShoes<T>
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x1
  // signature (TT;)V
  // declaration: void setShoesType(T)
  public setShoesType(Lcom/ancely/fanxing/demo2/Shoes;)V
   L0
    LINENUMBER 6 L0
    RETURN
   L1
    LOCALVARIABLE this Lcom/ancely/fanxing/demo2/NikeShoes; L0 L1 0
    // signature Lcom/ancely/fanxing/demo2/NikeShoes<TT;>;
    // declaration: com.ancely.fanxing.demo2.NikeShoes<T>
    LOCALVARIABLE t Lcom/ancely/fanxing/demo2/Shoes; L0 L1 1
    // signature TT;
    // declaration: T
    MAXSTACK = 0
    MAXLOCALS = 2

  // access flags 0x1
  // signature ()TT;
  // declaration: T getShoesType()
  public getShoesType()Lcom/ancely/fanxing/demo2/Shoes;
   L0
    LINENUMBER 9 L0
    ACONST_NULL
    ARETURN
   L1
    LOCALVARIABLE this Lcom/ancely/fanxing/demo2/NikeShoes; L0 L1 0
    // signature Lcom/ancely/fanxing/demo2/NikeShoes<TT;>;
    // declaration: com.ancely.fanxing.demo2.NikeShoes<T>
    MAXSTACK = 1
    MAXLOCALS = 1
}

很明顯擦除後,這個T變成了Shoes了

 

總結: 擦除總共做了三件事; 1: 判斷泛型有沒有限制,沒有限制則轉爲Object. 2: 如果有限制則轉爲相對應的類型; 3:有限制並實現了一個接口的話,會

  public synthetic bridge setShoesType(Ljava/lang/Object;)V
   L0
    LINENUMBER 3 L0
    ALOAD 0
    ALOAD 1
    CHECKCAST com/ancely/fanxing/demo2/Shoes//這句是強轉的意思
    INVOKEVIRTUAL com/ancely/fanxing/demo2/NikeShoes.setShoesType //這句是調用了setShoesType方法(Lcom/ancely/fanxing/demo2/Shoes;)V
    RETURN
   L1
    LOCALVARIABLE this Lcom/ancely/fanxing/demo2/NikeShoes; L0 L1 0
    // signature Lcom/ancely/fanxing/demo2/NikeShoes<TT;>;
    // declaration: com.ancely.fanxing.demo2.NikeShoes<T>
    MAXSTACK = 2
    MAXLOCALS = 2
}

多生成一個bridge方法在這個bridge方法中再調用相應的set方法.

爲什麼會多了一個set方法呢,是因爲一個接口在擦除泛型的時候,泛型沒有限制的時候會變成Object,而一個類去實現這個接口的時候,因爲這個類的泛型有限制,所以在擦除時不會是Object類型, 而在實現接口又一定要實現裏面的方法,所以需要一個Object類型的參數,所以就會多出一個帶Object參數的bridge方法.

還有一點,擦除完了的類會保存在類的常量池中,所以我們可以通過相應API獲取到泛型的類型.(File.getGenericType).

泛型的進階使用

    public static  <T> void appendS(List<T> list){
        T t = new T();
        list.add(t);
    }

比如上面這個泛型方法,這樣在代碼裏面肯定是報錯的,有什麼方法可以在裏面直接new一個T出來呢,就可以用下面這種方法

    public static <R> void append(List<R> list,Class<R> clazz){
        R r = null;
        try {
            r = clazz.newInstance();
            list.add(r);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章