《Java編程思想》第四版足足用了75頁來講泛型——厚厚的一沓內(nèi)容,很容易讓人頭大——但其實(shí)根本不用這么多,只需要一句話:我是一個(gè)泛型隊(duì)列,狗可以站進(jìn)來,貓也可以站進(jìn)來,但最好不要既站貓,又站狗!
清苑網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)!從網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、響應(yīng)式網(wǎng)站設(shè)計(jì)等網(wǎng)站項(xiàng)目制作,到程序開發(fā),運(yùn)營維護(hù)。創(chuàng)新互聯(lián)從2013年開始到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選創(chuàng)新互聯(lián)。
泛型,有人拆解這個(gè)詞為“參數(shù)化類型”。這種拆解其實(shí)也不好理解,還是按照沉默王二的意思來理解一下吧。
現(xiàn)在有一只玻璃杯,你可以讓它盛一杯白開水,也可以盛一杯二鍋頭——泛型的概念就在于此,制造這只杯子的時(shí)候沒必要在說明書上定義死,指明它只能盛白開水而不能盛二鍋頭!
可以在說明書上指明它用來盛裝液體,但最好也不要這樣,弄不好用戶想用它來盛幾塊冰糖呢!
這么一說,你是不是感覺不那么抽象了?泛型其實(shí)就是在定義類、接口、方法的時(shí)候不局限地指定某一種特定類型,而讓類、接口、方法的調(diào)用者來決定具體使用哪一種類型的參數(shù)。
就好比,玻璃杯的制造者說,我不知道使用者用這只玻璃杯來干嘛,所以我只負(fù)責(zé)造這么一只杯子;玻璃杯的使用者說,這就對(duì)了,我來決定這只玻璃杯是盛白開水還是二鍋頭,或者冰糖。
我們來看一段簡(jiǎn)短的代碼:
public class Cmower {
class Dog {
}
class Cat {
}
public static void main(String[] args) {
Cmower cmower = new Cmower();
Map map = new HashMap();
map.put("dog", cmower.new Dog());
map.put("cat", cmower.new Cat());
Cat cat = (Cat) map.get("dog");
System.out.println(cat);
}
}
這段代碼的意思是:我們?cè)趍ap中放了一只狗(Dog),又放了一只貓(Cat),當(dāng)我們想從map中取出貓的時(shí)候,卻一不留神把狗取了出來。
這段代碼編譯是沒有問題的,但運(yùn)行的時(shí)候就會(huì)報(bào)ClassCastException
(狗畢竟不是貓?。?/p>
Exception in thread "main" java.lang.ClassCastException: com.cmower.java_demo.sixteen.Cmower$Dog cannot be cast to com.cmower.java_demo.sixteen.Cmower$Cat
at com.cmower.java_demo.sixteen.Cmower.main(Cmower.java:20)
為什么會(huì)這樣呢?
1)寫代碼的程序員粗心大意。要從map中把貓取出來,你不能取狗啊!
2)創(chuàng)建map的時(shí)候,沒有明確指定map中要放的類型。如果指定是要放貓,那肯定取的時(shí)候就是貓,不會(huì)取出來狗;如果指定是要放狗,也一個(gè)道理。
第一種情況不太好解決,總不能把程序員打一頓(我可不想做一個(gè)天天背鍋的程序員,很重的好不好);第二種情況就比較容易解決,因?yàn)镸ap支持泛型(泛型接口)。
public interface Map<K,V> {
}
注:在Java中,經(jīng)常用T、E、K、V等形式的參數(shù)來表示泛型參數(shù)。
T:代表一般的任何類。
E:代表 Element 的意思,或者 Exception 異常的意思。
K:代表 Key 的意思。
V:代表 Value 的意思,通常與 K 一起配合使用。
既然Map支持泛型,那作為Map的實(shí)現(xiàn)者HashMap(泛型類)也支持泛型了。
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
}
其中的put方法(泛型方法)是這樣定義的:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
好了,現(xiàn)在使用泛型的形式來定義一個(gè)只能放Cat的Map吧!
public class Cmower {
class Dog {
}
class Cat {
}
public static void main(String[] args) {
Cmower cmower = new Cmower();
Map<String, Cat> map = new HashMap<>();
// map.put("dog", cmower.new Dog()); // 不再允許添加
map.put("cat", cmower.new Cat());
Cat cat = map.get("cat");
System.out.println(cat);
}
}
當(dāng)使用泛型定義map(鍵為String類型,值為Cat類型)后:
1)編譯器就不再允許你向map中添加狗的對(duì)象了。
2)當(dāng)你從map中取出貓的時(shí)候,也不再需要強(qiáng)制轉(zhuǎn)型了。
有人說,Java的泛型做的只是表面功夫——泛型信息存在于編譯階段(狗隊(duì)在編譯時(shí)不允許站貓),運(yùn)行階段就消失了(運(yùn)行時(shí)的隊(duì)列里沒有貓的信息,連狗的信息也沒有)——這種現(xiàn)象被稱為“類型擦除”。
來,看代碼解釋一下:
public class Cmower {
class Dog {
}
class Cat {
}
public static void main(String[] args) {
Cmower cmower = new Cmower();
Map<String, Cat> map = new HashMap<>();
Map<String, Dog> map1 = new HashMap<>();
// The method put(String, Cmower.Cat) in the type Map<String,Cmower.Cat> is not applicable for the arguments (String, Cmower.Dog)
//map.put("dog",cmower.new Dog());
System.out.println(map.getClass());
// 輸出:class java.util.HashMap
System.out.println(map1.getClass());
// 輸出:class java.util.HashMap
}
}
map的鍵位上是Cat,所以不允許put一只Dog;否則編譯器會(huì)提醒The method put(String, Cmower.Cat) in the type Map<String,Cmower.Cat> is not applicable for the arguments (String, Cmower.Dog)
。編譯器做得不錯(cuò),值得點(diǎn)贊。
但是問題就來了,map的Class類型為HashMap,map1的Class類型也為HashMap——也就是說,Java代碼在運(yùn)行的時(shí)候并不知道m(xù)ap的鍵位上放的是Cat,map1的鍵位上放的是Dog。
那么,試著想一些可怕的事情:既然運(yùn)行時(shí)泛型的信息被擦除了,而反射機(jī)制是在運(yùn)行時(shí)確定類型信息的,那么利用反射機(jī)制,是不是就能夠在鍵位為Cat的Map上放一只Dog呢?
我們不妨來試一試:
public class Cmower {
class Dog {
}
class Cat {
}
public static void main(String[] args) {
Cmower cmower = new Cmower();
Map<String, Cat> map = new HashMap<>();
try {
Method method = map.getClass().getDeclaredMethod("put",Object.class, Object.class);
method.invoke(map,"dog", cmower.new Dog());
System.out.println(map);
// {dog=com.cmower.java_demo.sixteen.Cmower$Dog@55f96302}
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
看到?jīng)]?我們竟然在鍵位為Cat的Map上放了一只Dog!
注:Java的設(shè)計(jì)者在JDK 1.5時(shí)才引入了泛型,但為了照顧以前設(shè)計(jì)上的缺陷,同時(shí)兼容非泛型的代碼,不得不做出了一個(gè)折中的策略:編譯時(shí)對(duì)泛型要求嚴(yán)格,運(yùn)行時(shí)卻把泛型擦除了——要兼容以前的版本,還要升級(jí)擴(kuò)展新的功能,真的很不容易!
有些時(shí)候,你會(huì)見到這樣一些代碼:
List<? extends Number> list = new ArrayList<>();
List<? super Number> list = new ArrayList<>();
?
和關(guān)鍵字extends
或者super
在一起其實(shí)就是泛型的高級(jí)應(yīng)用:通配符。
我們來自定義一個(gè)泛型類——PetHouse(寵物小屋),它有一些基本的動(dòng)作(可以住進(jìn)來一只寵物,也可以放出去):
public class PetHouse<T> {
private List<T> list;
public PetHouse() {
}
public void add(T item) {
list.add(item);
}
public T get() {
return list.get(0);
}
}
如果我們想要住進(jìn)去一只寵物,可以這樣定義小屋(其泛型為Pet):
PetHouse<Pet> petHouse = new PetHouse<>();
然后,我們讓小貓和小狗住進(jìn)去:
petHouse.add(new Cat());
petHouse.add(new Dog());
如果我們只想要住進(jìn)去一只小貓,打算這樣定義小屋:
PetHouse<Pet> petHouse = new PetHouse<Cat>();
但事實(shí)上,編譯器不允許我們這樣定義:因?yàn)榉盒筒恢苯又С窒蛏限D(zhuǎn)型。該怎么辦呢?
可以這樣定義小屋:
PetHouse<? extends Pet> petHouse = new PetHouse<Cat>();
也就是說,寵物小屋可以住進(jìn)去小貓,但它必須是寵物(Pet或者Pet的子類)而不是一只野貓。
但很遺憾,這個(gè)寵物小屋實(shí)際上住不了小貓,看下圖。
這是因?yàn)镴ava雖然支持泛型的向上轉(zhuǎn)型(使用 extends
通配符),但我們卻無法向其中添加任何東西——編譯器并不知道寵物小屋里要住的是小貓還是小狗,或者其他寵物,因此干脆什么都不讓住。
看到這,你一定非常疑惑,既然PetHouse<? extends Pet>
定義的寵物小屋什么也不讓住,那為什么還要這樣定義呢?思考一下。
泛型限定符有一描述:上界不存下界不取。
上界不存的原因:例如 List,編譯器只知道容器內(nèi)是 Father 及其子類,具體是什么類型并不知道,編譯器在看到 extends 后面的 Father 類,只是標(biāo)上一個(gè) CAP#1
作為占位符,無論往里面插什么,編譯器都不知道能不能和 CAP#1
匹配,所以就不允許插入。
extends的作用:可以在初始化的時(shí)候存入一個(gè)值,并且能保證數(shù)據(jù)的穩(wěn)定性,只能取不能存。讀取出來的數(shù)據(jù)可以存在父類或者基類里。
下界不取的原因:下界限定了元素的最小粒度,實(shí)際上是放松了容器元素的類型控制。例如 List, 元素是 Father 的基類,可以存入 Father 及其子類。但編譯器并不知道哪個(gè)是 Father 的超類,如 Human。讀取的時(shí)候,自然不知道是什么類型,只能返回 Object,這樣元素信息就全部丟失了。
super的作用:用于參數(shù)類型限定。
PECS 原則:
1.頻繁往外讀取內(nèi)容的,適合用extends
2.經(jīng)常往里插入的,適合用super
上一篇:Java 數(shù)組,看這篇就夠了
下一篇:HashMap,難的不在Map,而在Hash
微信搜索「沉默王二」公眾號(hào),關(guān)注后回復(fù)「免費(fèi)視頻」獲取 500G 高質(zhì)量教學(xué)視頻(已分門別類)。
新聞名稱:Java泛型的重要目的:別讓貓別站在狗隊(duì)里
URL地址:http://jinyejixie.com/article0/posjoo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供標(biāo)簽優(yōu)化、動(dòng)態(tài)網(wǎng)站、外貿(mào)網(wǎng)站建設(shè)、響應(yīng)式網(wǎng)站、網(wǎng)站排名、ChatGPT
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)