一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java中數組協變和范型不變性踩坑記錄

Java中數組協變和范型不變性踩坑記錄

2021-07-17 11:19左之了 Java教程

數組的協變性來源于數組的一個優勢,這篇文章主要給大家介紹了關于Java中數組協變和范型不變性踩坑的一些內容,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面來一起學

前言

變性是oop語言不變的大坑,java的數組協變就是其中的一口老坑。因為最近踩到了,便做一個記錄。順便也提一下范型的變性。

解釋數組協變之前,先明確三個相關的概念,協變、不變和逆變。

下面話不多說了,來一起看看詳細的介紹吧

一、協變、不變、逆變

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

?
1
2
3
4
5
6
7
class soup<t> {
 public void add(t t) {}
}
 
class vegetable { }
 
class carrot extends vegetable { }

有一個范型類soup<t>,表示用食材t做的湯,它的方法add(t t)表示向湯中添加食材t。類vegetable表示蔬菜,類carrot表示胡蘿卜。當然,carrot是vegetable的子類。

那么問題來了,soup<vegetable>和soup<carrot>之間是什么關系呢?

第一反應,soup<carrot>應該是soup<vegetable>的子類,因為胡蘿卜湯顯然是一種蔬菜湯。如果真是這樣,那就看看下面的代碼。其中tomato表示西紅柿,是vegetable的另一個子類

?
1
2
soup<vegetable> soup = new soup<carrot>();
soup.add(new tomato());

第一句沒問題,soup<carrot>是soup<vegetable>的子類,所以可以將soup<carrot>的實例賦給變量soup。第二句也沒問題,因為soup聲明為soup<vegetable>類型,它的add方法接收一個vegetable類型的參數,而tomato是vegetable,類型正確。

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

那么,soup<carrot>和soup<vegetable>究竟應該是什么關系呢?不同的語言有不同的理解和實現。總結起來,有三種情況。

(1)如果soup<carrot>是soup<vegetable>的子類,則稱泛型soup<t>是協變的

(2)如果soup<carrot>和soup<vegetable>是無關的兩個類,則稱泛型soup<t>是不變的

(3)如果soup<carrot>是soup<vegetable>的父類,則稱泛型soup<t>是逆變的。(不過逆變不常見)

理解了協變、不變和逆變的概念,再看java的實現。java的一般泛型是不變的,也就是說soup<vegetable>和soup<carrot>是毫無關系的兩個類,不能將一個類的實例賦值給另一個類的變量。所以,上面那段用西紅柿做胡蘿卜湯的代碼,其實根本無法通過編譯。

二、數組協變

java中,數組是基本類型,不是泛型,不存在array<t>這樣的東西。但它和泛型很像,都是用另一個類型構建的類型。所以,數組也是要考慮變性的。

與泛型的不變性不同,java的數組是協變的。也就是說,carrot[]是vegetable[]的子類。而上一節中的例子已經表明,協變有時會引發問題。比如下面這段代碼

?
1
2
vegetable[] vegetables = new carrot[10];
vegetables[0] = new tomato(); // 運行期錯誤

因為數組是協變的,編譯器允許把carrot[10]賦值給vegetable[]類型的變量,所以這段代碼可以順利通過編譯。只有在運行期,jvm真的試圖往一堆胡蘿卜中插入一個西紅柿的時候,才發現大事不好。所以,上面的代碼在運行期會拋出一個java.lang.arraystoreexception類型的異常。

數組協變性,是java的著名歷史包袱之一。使用數組時,千萬要小心!

如果把例子中的數組替換為list,情況就不同了。就像這樣

?
1
2
arraylist<vegetable> vegetables = new arraylist<carrot>(); // 編譯期錯誤
vegetables.add(new tomato());

arraylist是一個泛型類,它是不變的。所以,arraylist<carrot>和arraylist<vegetable>之間并無繼承關系,這段代碼在編譯期就會報錯。

兩段代碼雖然都會報錯,但通常情況下,編譯期錯誤總比運行期錯誤好處理一些。

三、當泛型也想要協變、逆變

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

?
1
2
3
class girl {
 public void drink(soup<vegetable> soup) {}
}

我們希望drink方法可以接受各種不同的蔬菜湯,包括soup<carrot>和soup<tomato>。但受到不變性的限制,它們無法作為drink的參數。

要實現這一點,應該采用一種類似于協變性的寫法

?
1
public void drink(soup<? extends vegetable> soup) {}

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

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

?
1
2
3
4
public void drink(soup<? extends vegetable> soup) {
 soup.add(new tomato()); // 錯誤
 soup.add(null); // 正確
}

方法內的第一句會在編譯期報錯。因為編譯器只知道add方法的參數是vegetable的子類,卻不知道它具體是carrot、tomato、或者其他的什么類型。這時,傳遞一個具體類型的實例一律被視為不安全的。即使soup真的是soup<tomato>類型也不行,因為soup的具體類型信息是在運行期才能知道的,編譯期并不知道。

但是方法內的第二句是正確的。因為參數是null,它可以是任何合法的類型。編譯器認為它是安全的。

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

?
1
public void drink(soup<? super vegetable> soup) {}

這時,soup<t>中的t必須是vegetable的父類。

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

?
1
2
3
public void drink(soup<? super vegetable> soup) {
 soup.add(new tomato());
}

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

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。

原文鏈接:https://www.cnblogs.com/tjxing/p/10419993.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 精品精品国产自在久久高清 | 麻豆网站视频国产在线观看 | 成人精品在线 | 天堂资源在线www中文 | 国产乱码免费卡1卡二卡3卡四 | 成人在线免费播放 | 日韩精品 欧美 | 边摸边吃奶边做爽视频免费 | 成人xxxxxx| 国产成人精品视频一区二区不卡 | 成人免费在线视频观看 | 2019中文字幕在线视频 | 天美传媒传媒免费观看 | 无颜之月全集免费观看 | 国产三区二区 | 日本最新伦中文字幕 | 武侠艳妇屈辱的张开双腿 | 国产高清视频网站 | 欧美腐剧mm在线观看 | 日韩精品视频免费 | 国产精品久久国产三级国电话系列 | 99精品视频免费观看 | 强制高h | 精品久久久久久午夜 | chinesehdxxx吃奶水| 91精品国产色综合久久不卡蜜 | 青青青久热国产精品视频 | 精品一区二区三区高清免费观看 | 免费黄色网站视频 | 免费日批| 美女被视频网站看免费入口 | 欧美日韩国产亚洲人成 | 污网站免费观看在线高清 | 欧美a级在线观看 | 精品久久久久久无码人妻国产馆 | 国色天香社区在线 | 耽美双性 | 疯狂激吻添下边小说 | 亚洲成年网站在线观看 | 美味情缘韩国在线观看视频 | 国产一区二区视频在线观看 |