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();
}
}