真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Java中數(shù)組協(xié)變和范型不變性的示例分析

這篇文章將為大家詳細(xì)講解有關(guān)Java中數(shù)組協(xié)變和范型不變性的示例分析,小編覺得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。

為宿遷等地區(qū)用戶提供了全套網(wǎng)頁設(shè)計(jì)制作服務(wù),及宿遷網(wǎng)站建設(shè)行業(yè)解決方案。主營業(yè)務(wù)為網(wǎng)站建設(shè)、網(wǎng)站設(shè)計(jì)、宿遷網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務(wù)。我們深信只要達(dá)到每一位用戶的要求,就會(huì)得到認(rèn)可,從而選擇與我們長期合作。這樣,我們也可以走得更遠(yuǎn)!

一、協(xié)變、不變、逆變

假設(shè),我為一家餐館寫了這樣一段代碼

class Soup {
 public void add(T t) {}
}

class Vegetable { }

class Carrot extends Vegetable { }

有一個(gè)范型類Soup,表示用食材T做的湯,它的方法add(T t)表示向湯中添加食材T。類Vegetable表示蔬菜,類Carrot表示胡蘿卜。當(dāng)然,Carrot是Vegetable的子類。

那么問題來了,Soup和Soup之間是什么關(guān)系呢?

第一反應(yīng),Soup應(yīng)該是Soup的子類,因?yàn)楹}卜湯顯然是一種蔬菜湯。如果真是這樣,那就看看下面的代碼。其中Tomato表示西紅柿,是Vegetable的另一個(gè)子類

Soup soup = new Soup();
soup.add(new Tomato());

第一句沒問題,Soup是Soup的子類,所以可以將Soup的實(shí)例賦給變量soup。第二句也沒問題,因?yàn)閟oup聲明為Soup類型,它的add方法接收一個(gè)Vegetable類型的參數(shù),而Tomato是Vegetable,類型正確。

但是,兩句放在一起卻有了問題。soup的實(shí)際類型是Soup,而我們給它的add方法傳遞了一個(gè)Tomato的實(shí)例!換言之,我們在用西紅柿做胡蘿卜湯,肯定做不出來。所以,把Soup視為Soup的子類在邏輯上雖然是通順的,在使用過程中卻是有缺陷的。

那么,Soup和Soup究竟應(yīng)該是什么關(guān)系呢?不同的語言有不同的理解和實(shí)現(xiàn)。總結(jié)起來,有三種情況。

(1)如果Soup是Soup的子類,則稱泛型Soup是協(xié)變的

(2)如果Soup和Soup是無關(guān)的兩個(gè)類,則稱泛型Soup是不變的

(3)如果Soup是Soup的父類,則稱泛型Soup是逆變的。(不過逆變不常見)

理解了協(xié)變、不變和逆變的概念,再看Java的實(shí)現(xiàn)。Java的一般泛型是不變的,也就是說Soup和Soup是毫無關(guān)系的兩個(gè)類,不能將一個(gè)類的實(shí)例賦值給另一個(gè)類的變量。所以,上面那段用西紅柿做胡蘿卜湯的代碼,其實(shí)根本無法通過編譯。

二、數(shù)組協(xié)變

Java中,數(shù)組是基本類型,不是泛型,不存在Array這樣的東西。但它和泛型很像,都是用另一個(gè)類型構(gòu)建的類型。所以,數(shù)組也是要考慮變性的。

與泛型的不變性不同,Java的數(shù)組是協(xié)變的。也就是說,Carrot[]是Vegetable[]的子類。而上一節(jié)中的例子已經(jīng)表明,協(xié)變有時(shí)會(huì)引發(fā)問題。比如下面這段代碼

Vegetable[] vegetables = new Carrot[10];
vegetables[0] = new Tomato(); // 運(yùn)行期錯(cuò)誤

因?yàn)閿?shù)組是協(xié)變的,編譯器允許把Carrot[10]賦值給Vegetable[]類型的變量,所以這段代碼可以順利通過編譯。只有在運(yùn)行期,JVM真的試圖往一堆胡蘿卜中插入一個(gè)西紅柿的時(shí)候,才發(fā)現(xiàn)大事不好。所以,上面的代碼在運(yùn)行期會(huì)拋出一個(gè)java.lang.ArrayStoreException類型的異常。

數(shù)組協(xié)變性,是Java的著名歷史包袱之一。使用數(shù)組時(shí),千萬要小心!

如果把例子中的數(shù)組替換為List,情況就不同了。就像這樣

ArrayList vegetables = new ArrayList(); // 編譯期錯(cuò)誤
vegetables.add(new Tomato());

ArrayList是一個(gè)泛型類,它是不變的。所以,ArrayList和ArrayList之間并無繼承關(guān)系,這段代碼在編譯期就會(huì)報(bào)錯(cuò)。

兩段代碼雖然都會(huì)報(bào)錯(cuò),但通常情況下,編譯期錯(cuò)誤總比運(yùn)行期錯(cuò)誤好處理一些。

三、當(dāng)泛型也想要協(xié)變、逆變

泛型是不變的,但某些場景里我們還是希望它能協(xié)變起來。比如,有一個(gè)天天喝蔬菜湯減肥的小姐姐

class Girl {
 public void drink(Soup soup) {}
}

我們希望drink方法可以接受各種不同的蔬菜湯,包括Soup和Soup。但受到不變性的限制,它們無法作為drink的參數(shù)。

要實(shí)現(xiàn)這一點(diǎn),應(yīng)該采用一種類似于協(xié)變性的寫法

public void drink(Soup soup) {}

意思是,參數(shù)soup的類型是泛型類Soup,而T是Vegetable的子類(也包括Vegetable自己)。這時(shí),小姐姐終于可以愉快地喝上胡蘿卜湯和西紅柿湯了。

但是,這種方法有一個(gè)限制。編譯器只知道泛型參數(shù)是Vegetable的子類,卻不知道它具體是什么。所以,所有非null的泛型類型參數(shù)均被視為不安全的。說起來很拗口,其實(shí)很簡單。直接上代碼

public void drink(Soup soup) {
 soup.add(new Tomato()); // 錯(cuò)誤
 soup.add(null); // 正確
}

方法內(nèi)的第一句會(huì)在編譯期報(bào)錯(cuò)。因?yàn)榫幾g器只知道add方法的參數(shù)是Vegetable的子類,卻不知道它具體是Carrot、Tomato、或者其他的什么類型。這時(shí),傳遞一個(gè)具體類型的實(shí)例一律被視為不安全的。即使soup真的是Soup類型也不行,因?yàn)閟oup的具體類型信息是在運(yùn)行期才能知道的,編譯期并不知道。

但是方法內(nèi)的第二句是正確的。因?yàn)閰?shù)是null,它可以是任何合法的類型。編譯器認(rèn)為它是安全的。

同樣,也有一種類似于逆變的方法

public void drink(Soup soup) {}

這時(shí),Soup中的T必須是Vegetable的父類。

這種情況就不存在上面的限制了,下面的代碼毫無問題

public void drink(Soup soup) {
 soup.add(new Tomato());
}

Tomato是Vegetable的子類,自然也是Vegetable父類的子類。所以,編譯期就可以確定類型是安全的。

關(guān)于“Java中數(shù)組協(xié)變和范型不變性的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學(xué)到更多知識,如果覺得文章不錯(cuò),請把它分享出去讓更多的人看到。


分享文章:Java中數(shù)組協(xié)變和范型不變性的示例分析
鏈接URL:http://weahome.cn/article/geecoi.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部