Java 泛型(generics)是 JDK1.5 中引入的一個(gè)新特性领炫,其本質(zhì)是參數(shù)化類(lèi)型,解決不確定具體對(duì)象類(lèi)型的問(wèn)題;其所操作的數(shù)據(jù)類(lèi)型被指定為一個(gè)參數(shù)(type parameter)這種參數(shù)類(lèi)型可以用在類(lèi)硝逢、接口和方法的創(chuàng)建中,分別稱(chēng)為泛型類(lèi)炊甲、泛型接口鱼炒、泛型方法衔沼。
但是在 Java 中并不是真正的泛型,實(shí)際上是“偽泛型”
類(lèi)型擦除(type Erasure)
為了與之前的版本兼容,JDK1.5 中通過(guò)類(lèi)型擦除來(lái)增加的泛型功能指蚁。Java 泛型只是在編譯器層次上菩佑,在編譯后生成的字節(jié)碼中是不包含泛型中類(lèi)型的信息的。
通過(guò)一個(gè)例子來(lái)證明類(lèi)型擦除
public class main {
public static void main(String[] args) {
ArrayList<String> sList = new ArrayList<String>();
ArrayList<Integer> iList = new ArrayList<Integer>();
System.out.println(sList.getClass() == iList.getClass());
}
}
上面定義了兩個(gè) ArrayList凝化,一個(gè)是 ArrayList<String>泛型類(lèi)型的,一個(gè)是 ArrayList<Integer>類(lèi)型的稍坯,但是最后打印的是true
,說(shuō)明兩個(gè)類(lèi)型相同搓劫。
用javap -c
看一下生成的生成的字節(jié)碼
可以看到在字節(jié)碼中劣光,ArrayList<String>和 ArrayList<Integer>都被編譯成了 ArrayList 類(lèi)型,可見(jiàn)編譯后發(fā)生了類(lèi)型擦除糟把。
- 既然編譯后發(fā)生了類(lèi)型擦除,那么虛擬機(jī)解析牲剃、反射等場(chǎng)景是怎么獲取到正確的類(lèi)型的遣疯?
在 JDk1.5 中增加泛型的同時(shí),JCP 組織修改了虛擬機(jī)規(guī)范凿傅,增加了Signature
缠犀、LocalVariableTypeTable
新屬性。
用javap -v
查看一下字節(jié)碼聪舒,在main
方法中包含一段
LocalVariableTypeTable:
Start Length Slot Name Signature
8 31 1 sList Ljava/util/ArrayList<Ljava/lang/String;>;
16 23 2 iList Ljava/util/ArrayList<Ljava/lang/Integer;>;
LocalVariableTypeTable
是一個(gè)可選屬性辨液,如果存在泛型,則會(huì)出現(xiàn)這個(gè)屬性箱残。在Signature
下包含了泛型的信息滔迈。
- 接下來(lái),看這段代碼
ArrayList<String> sList = new ArrayList<String>();
sList.add("111");
String s = sList.get(0);
類(lèi)型擦除之后被辑,當(dāng)調(diào)用sList.get(0)
是如何確保返回的值不會(huì)和 String 不匹配呢燎悍?
用javap -c
查看一下字節(jié)碼
public class com.example.demo.test.main {
// .....省略
public static void main(java.lang.String[]) throws java.lang.NoSuchFieldException;
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String 111
11: invokevirtual #5 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
14: pop
15: aload_1
16: iconst_0
17: invokevirtual #6 // Method java/util/ArrayList.get:(I)Ljava/lang/Object;
20: checkcast #7 // class java/lang/String
23: astore_2
24: return
}
在#7
處有一個(gè)checkcast
指令,checkcast
用于檢查類(lèi)型強(qiáng)制轉(zhuǎn)換是否可以進(jìn)行盼理,也就是泛型在獲取值的時(shí)候進(jìn)行了強(qiáng)制類(lèi)型轉(zhuǎn)換谈山。
- 再來(lái)看看下面這段代碼
首先定義一個(gè) Java 泛型類(lèi)
public class GenericClass<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
再定義一個(gè)子類(lèi)繼承它
public class GenericClassTest extends GenericClass<Integer> {
@Override
public void setValue(Integer value) {
super.setValue(value);
}
@Override
public Integer getValue(){
return super.getValue();
}
}
在GenericClassTest
中將GenericClass
的泛型定義為Integer
類(lèi)型,并重寫(xiě)了 get 和 set 方法宏怔,因?yàn)榇嬖陬?lèi)型擦除奏路,父類(lèi)GenericClass
的泛型被擦除了。
用javap -c
查看一下GenericClass
編譯后的字節(jié)碼
可以看到類(lèi)型擦除后泛型變?yōu)榱?code>Object臊诊。那么GenericClass
也就變?yōu)榱?/p>
public class GenericClass {
private Object value;
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
這樣鸽粉,父類(lèi)GenericClass
中 set 和 get 方法操作的是 Object 對(duì)象,而子類(lèi)GenericClassTest
操作的是 Integer 對(duì)象妨猩,為什么還可以重寫(xiě)潜叛?按照正常的繼承關(guān)系中,這應(yīng)該是重載。
按照重載的方式試一下
可以看到設(shè)置 Object 對(duì)象出現(xiàn)了紅波浪線(xiàn)威兜,不允許這樣設(shè)置销斟,看來(lái)確實(shí)是重寫(xiě),而不是重載椒舵。為什么會(huì)時(shí)重寫(xiě)蚂踊,這不是跟 Java 多態(tài)沖突么?繼續(xù)往下研究笔宿。
現(xiàn)在用javap -c
看一下子類(lèi)GenericClassTest
的字節(jié)碼文件
在GenericClassTest
中 get 和/set 方法都有兩個(gè)犁钟,一個(gè)是操作 Object 對(duì)象一個(gè)是操作 Integer 對(duì)象。
操作 Integer 對(duì)象的是GenericClassTest
定義的泼橘,操作 Object 對(duì)象的是由編譯器生成的涝动。
再用javap -v
查看一下字節(jié)碼更詳細(xì)的信息。
編譯器生成的兩個(gè)操作 Object 對(duì)象的方法中多了兩個(gè)ACC_BRIDGE
炬灭、ACC_SYNTHETIC
標(biāo)志醋粟。
這就是虛擬機(jī)解決類(lèi)型擦除和多態(tài)沖突問(wèn)題的方法:使用橋接方法
。
橋接方法
方法是由編譯器生成的重归,我們?cè)诖a中并不能直接使用米愿,但是可以通過(guò)反射拿到橋接方法再使用。
泛型一旦編譯過(guò)后鼻吮,類(lèi)型就被擦除了育苟,那到了運(yùn)行時(shí),怎么獲取泛型信息椎木?這就要使用 JDK 提供的 Type 類(lèi)型接口了违柏。
Type 類(lèi)型
在沒(méi)有泛型之前,所有的類(lèi)型都通過(guò) Class 類(lèi)進(jìn)行抽象香椎,Class 類(lèi)的一個(gè)具體對(duì)象就代表了一個(gè)類(lèi)型勇垛。
在 JDK1.5 增加了泛型之后,擴(kuò)充了數(shù)據(jù)類(lèi)型士鸥,將泛型也包含了闲孤。
JDK 在原來(lái)的基礎(chǔ)上增加了一個(gè)Type
接口,它是所有類(lèi)型的父接口烤礁,它的子類(lèi)有
-
Class
類(lèi): 原始/基本類(lèi)型讼积,包括平時(shí)我們所有的類(lèi)、枚舉脚仔、數(shù)組勤众、注解,還有 int鲤脏、float 等基本類(lèi)型 -
ParameterizedType
接口:參數(shù)化類(lèi)型们颜,比如 List<String> -
TypeVariable
接口:類(lèi)型變量吕朵,比如 List<T>中的 T 就是參數(shù)化變量 -
GenericArrayType
接口: 數(shù)組類(lèi)型,比如 List<String>[]窥突、T[] -
WildcardType
接口:泛型表達(dá)式類(lèi)型努溃,比如 List< ? extends Number>
ParameterizedType
參數(shù)化類(lèi)型,即帶有參數(shù)的類(lèi)型阻问,也就是帶有<>的類(lèi)型
public interface ParameterizedType extends Type {
Type[] getActualTypeArguments();
Type getRawType();
Type getOwnerType();
}
-
getActualTypeArguments()
: 獲取類(lèi)型內(nèi)部的參數(shù)化類(lèi)型 比如 Map<K,V>里面的 K梧税,V 類(lèi)型。 -
getRawType()
: 類(lèi)的原始類(lèi)型称近,比如 Map<K,V>中的 Map 類(lèi)型第队。 -
getOwnerType()
: 獲取所有者類(lèi)型(只有內(nèi)部類(lèi)才有所有者,比如 Map.Entry 他的所有者就是 Map)刨秆,若不是內(nèi)部類(lèi)凳谦,此處返回 null。
實(shí)例:
public class GenericClass<T> {
private List<String> list;
private List<T> tList;
public static void main(String[] args) {
Class<GenericClass> genericClassClass = GenericClass.class;
Field[] declaredFields = genericClassClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
Type genericType = declaredField.getGenericType();
if (genericType instanceof ParameterizedType) {
System.out.println("==========" + genericType.getTypeName() + "======ParameterizedType類(lèi)型=====");
ParameterizedType parameterizedType = (ParameterizedType) genericType;
System.out.println("getActualTypeArguments:");
Type[] actualTypeArguments = (parameterizedType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(" " + actualTypeArgument);
}
Type rawType = (parameterizedType).getRawType();
System.out.println("getRawType:");
System.out.println(" " + rawType);
}
}
}
}
輸出
==========java.util.List<java.lang.String>======ParameterizedType類(lèi)型=====
getActualTypeArguments:
java.lang.String
getRawType:
interface java.util.List
==========java.util.List<T>======ParameterizedType類(lèi)型=====
getActualTypeArguments:
T
getRawType:
interface java.util.List
TypeVariable
類(lèi)型變量衡未,即泛型中的變量晾蜘,例如:T、K眠屎、V 等變量,可以表示任何類(lèi)肆饶;
注意: 與 ParameterizedType 的區(qū)別改衩,TypeVariable 代表著泛型中的變量,而 ParameterizedType 則代表整個(gè)泛型驯镊。比如 List<T>中葫督,T 是 TypeVariable 類(lèi)型,List<T>是 ParameterizedType 類(lèi)型
public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {
Type[] getBounds();
D getGenericDeclaration();
String getName();
// JDK8新增的
AnnotatedType[] getAnnotatedBounds();
}
-
getBounds()
:類(lèi)型對(duì)應(yīng)的上限板惑,默認(rèn)為 Object 可以有多個(gè)橄镜。比如 List< T extends Number & Serializable>中的 Number 和 Serializable -
getGenericDeclaration()
: 獲取聲明該類(lèi)型變量實(shí)體,比如 GenericClass< T>中的 GenericClass -
getName()
:獲取類(lèi)型變量在源碼中定義的名稱(chēng)冯乘;
實(shí)例:
public class GenericClass<T extends Number> {
private T t;
public static void main(String[] args) {
Class<GenericClass> genericClassClass = GenericClass.class;
Field[] declaredFields = genericClassClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
Type genericType = declaredField.getGenericType();
if (genericType instanceof TypeVariable) {
System.out.println("==========" + genericType.getTypeName() + "======TypeVariable類(lèi)型=====");
TypeVariable typeVariable = (TypeVariable) genericType;
Type[] bounds = typeVariable.getBounds();
System.out.println("getBounds:");
for (Type bound : bounds) {
System.out.println(" " + bound);
}
System.out.println("getGenericDeclaration:");
System.out.println(" " + typeVariable.getGenericDeclaration());
System.out.println("getName:");
System.out.println(" " + typeVariable.getName());
}
}
}
}
輸出:
==========T======TypeVariable類(lèi)型=====
getBounds:
class java.lang.Number
getGenericDeclaration:
class com.example.demo.test.GenericClass
getName:
T
GenericArrayType
泛型數(shù)組類(lèi)型洽胶,用來(lái)描述 ParameterizedType、TypeVariable 類(lèi)型的數(shù)組裆馒;例如:List<T>[] 姊氓、T[]、List<String>[]等喷好。
注意: GenericArrayType 是來(lái)描述與泛型相關(guān)的數(shù)組翔横,與 String[]、int[]梗搅、float[]這種類(lèi)型不同禾唁。
public interface GenericArrayType extends Type {
Type getGenericComponentType();
}
-
getGenericComponentType()
:返回泛型數(shù)組中元素的 Type 類(lèi)型效览,比如 List<String>[] 中的 List<String>
實(shí)例:
public class GenericClass<T extends Number> {
private List<String>[] lists;
private T[] ts;
public static void main(String[] args) {
Class<GenericClass> genericClassClass = GenericClass.class;
Field[] declaredFields = genericClassClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
Type genericType = declaredField.getGenericType();
if (genericType instanceof GenericArrayType) {
GenericArrayType genericArrayType = (GenericArrayType) genericType;
System.out.println("==========" + genericType.getTypeName() + "======GenericArrayType類(lèi)型=====");
Type genericComponentType = genericArrayType.getGenericComponentType();
System.out.println("getGenericComponentType:");
System.out.println(" " + genericComponentType);
}
}
}
}
輸出:
==========java.util.List<java.lang.String>[]======GenericArrayType類(lèi)型=====
getGenericComponentType:
java.util.List<java.lang.String>
==========T[]======GenericArrayType類(lèi)型=====
getGenericComponentType:
T
WildcardType
泛型表達(dá)式(通配符表達(dá)式)。例如:荡短? extend Number丐枉、? super Integer肢预。
注意: WildcardType 雖然是 Type 的子接口矛洞,但不代表一種類(lèi)型,烫映,表示的僅僅是類(lèi)似 ? extends T沼本、? super K 這樣的通配符表達(dá)式。
public interface WildcardType extends Type {
Type[] getUpperBounds();
Type[] getLowerBounds();
}
-
getUpperBounds()
獲得泛型表達(dá)式上界(上限) 獲取泛型變量的上邊界(extends) -
getLowerBounds()
獲得泛型表達(dá)式下界(下限) 獲取泛型變量的下邊界(super)
實(shí)例:
public class GenericClass<T extends Number> {
private List<? extends Number> numbers;
private List<? super Integer> integers;
public static void main(String[] args) {
Class<GenericClass> genericClassClass = GenericClass.class;
Field[] declaredFields = genericClassClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
Type genericType = declaredField.getGenericType();
if (genericType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) genericType;
Type[] actualTypeArguments = (parameterizedType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
if(actualTypeArgument instanceof WildcardType){
System.out.println("==========" + actualTypeArgument.getTypeName() + "======WildcardType類(lèi)型=====");
WildcardType wildcardType = (WildcardType) actualTypeArgument;
System.out.println("getUpperBounds:");
Type[] upperBounds = wildcardType.getUpperBounds();
for (Type upperBound : upperBounds) {
System.out.println(" "+ upperBound);
}
System.out.println("getLowerBounds:");
Type[] lowerBounds = wildcardType.getLowerBounds();
for (Type lowerBound : lowerBounds) {
System.out.println(" "+ lowerBound);
}
}
}
}
}
}
}
輸出:
==========? extends java.lang.Number======WildcardType類(lèi)型=====
getUpperBounds:
class java.lang.Number
getLowerBounds:
==========? super java.lang.Integer======WildcardType類(lèi)型=====
getUpperBounds:
class java.lang.Object
getLowerBounds:
class java.lang.Integer