-
通用通配符
-
類型擦除
通用通配符
泛型中有三種通配符形式:
-
<?>
:無限制通配符類型 -
<? extends E> extends
:關(guān)鍵字聲明了類型的上界吻商,表示參數(shù)化的類型可能是所指定的類型往声,或者是此類型的子類。 -
<? super E> super
:關(guān)鍵字聲明了類型的下界嗦玖,表示參數(shù)化類型可能是指定類型片橡,或者是此類型的父類妈经。
通配符在聲明局部變量時是沒有什么意義的,通配符一般是用作為一個方法聲明參數(shù)。
通配符在聲明局部變量與為方法聲明參數(shù)代碼:
package com.wechat.management.type;
import java.util.ArrayList;
import java.util.List;
class Fruit{}
class Apple extends Fruit{}
class Orange extends Fruit{}
public class TestGenericStatement{
public static void main(String[] args) {
// 聲明局部變量類型時 <? extends Type> 中的 ? 只能是null
List<? extends Fruit> list = new ArrayList<>();
list.add(new Apple());//不可添加
list.add(new Orange());//報錯 : add (capture<? extends com.wechat.management.type.Fruit>) in List cannot be applied to (com.wechat.management.type.Orange)
list.add(null);// ? extends Fruit 只能添加null(無參數(shù)類型)
list.contains(new Apple());//可以執(zhí)行是因為入?yún)镺bject o
list.indexOf(new Apple());//可以執(zhí)行是因為入?yún)镺bject o
// 聲明局部變量類型時 <? super Type> 中的 ? 是指Type的子類和自身
List<? super Fruit> list2 = new ArrayList<>();
list2.add(new Apple());
list2.add(new Orange());
list2.add(new Fruit());
list2.add(new Object());//報錯 : add (capture<? super com.wechat.management.type.Fruit>) in List cannot be applied to (java.lang.Object)
//--------------------------------------------------
List<Apple> appleList = new ArrayList<>();
List<Object> objectList = new ArrayList<>();
// 作為方法入?yún)r吹泡,<? super Type> 中的 ? 是指Type的父類
addSuper(appleList);// 報錯:cannot be applied
addSuper(objectList);// 可以添加 Type的父類
// 作為方法入?yún)r骤星,<? extends Type> 中的 ? 是指Type的子類
addExtends(appleList);// 可以添加 Type的子類
addExtends(objectList);// 報錯:cannot be applied
List<Fruit> fruitList = new ArrayList<>();
addSuper(fruitList);// 可以添加 包含自身
addExtends(fruitList);// 可以添加 包含自身
}
public static void addSuper(List<? super Fruit> list) {}
public static void addExtends(List<? extends Fruit> list) {}
}
總結(jié):
- 聲明局部變量無意義,不要使用泛型通配符
- 聲明方法參數(shù)類型時爆哑,
<? super Type>
中的 ? 是Type的父類洞难,<? extends Type>
中的 ? 是Type的子類。
注:介紹下 List<? extends Fruit> 聲明成員變量時為什么不能add任何類型揭朝,甚至Object都不行队贱,除了null,因為null代表任何類型潭袱。
import java.util.ArrayList;
import java.util.List;
class Fruit{}
class Apple extends Fruit{}
class Orange extends Fruit{}
public class TestGenericStatement{
public static void main(String[] args) {
List<? extends Fruit> list = new ArrayList<>();
// list.add(new Apple());//不可添加
// list.add(new Orange());//報錯 : add (capture<? extends com.wechat.management.type.Fruit>) in List cannot be applied to (com.wechat.management.type.Orange)
list.add(null);// ? extends Fruit 只能添加null(無參數(shù)類型)
list.contains(new Apple());//可以執(zhí)行是因為入?yún)镺bject o
list.indexOf(new Apple());//可以執(zhí)行是因為入?yún)镺bject o
}
}
不能add任何類型原因解析:
List< ? extends Fruit>
可以解讀為:具有任何從Fruit繼承的類型柱嫌,但實際上,它意味著屯换,它沒有指定具體類型编丘。對于編譯器來說,當(dāng)你指定了一個List< ? extends Fruit>
彤悔,add的參數(shù)也變成了? extends Fruit
瘪吏。因此編譯器并不能了解這里到底需要哪種Fruit的子類型,因此編譯器不接受任何類型的參數(shù)蜗巧。
類型擦除
當(dāng)編譯器對帶有泛型的java代碼進(jìn)行編譯時掌眠,它會去執(zhí)行類型檢查和類型推斷,然后生成普通的不帶泛型的字節(jié)碼幕屹,這種普通的字節(jié)碼可以被一般的 Java 虛擬機接收并執(zhí)行蓝丙,這在就叫做類型擦除(type erasure)
Java中的泛型僅僅是給編譯器javac使用的,確保數(shù)據(jù)的安全性和免去強制類型轉(zhuǎn)換的問題望拖,但是渺尘,一旦編譯完成,所有和泛型有關(guān)的類型全部擦除说敏。
通過反射查看編譯后代碼:
import java.lang.reflect.Method;
import java.util.ArrayList;
public class InvokeDemo {
public static void main(String[] args) throws Exception {
ArrayList<Integer> strList = new ArrayList<>();
strList.add(100);
strList.add(200);
//獲取ArrayList的Class對象鸥跟,反向的調(diào)用add()方法,添加數(shù)據(jù)
Class listClass = strList.getClass(); //得到 strList 對象的字節(jié)碼 對象
//獲取add()方法
Method method = listClass.getMethod("add", Object.class);
//調(diào)用add()方法
method.invoke(strList, "數(shù)字");
//遍歷集合
for (Object obj : strList) {
System.out.println(obj);
}
}
}
執(zhí)行結(jié)果:
100
200
數(shù)字
ArrayList<Integer> strList = new ArrayList<>();
strList.add(100);
strList.add(200);
System.out.println("Hello World!");
以上代碼反編譯后的代碼為:
擴(kuò)展閱讀:為什么Java的泛型要用"擦除"實現(xiàn)