Java為什么引入泛型
??????眾所周知糠悯,Java 5才最大的亮點(diǎn)就是引入泛型,那么Java引入泛型的目的是什么妻往?這就需要查看Java 5引入泛型前的代碼:(因?yàn)镴ava向后兼容互艾,現(xiàn)在這段代碼還能編譯成功)
#daqiJava.java
List list = new ArrayList();
list.add("");
String str = (String) list.get(0);
//添加錯(cuò)誤類型
list.add(1);
??????由于ArrayList
底層是依靠Object數(shù)組
實(shí)現(xiàn)的,這使得任何類型都可以添加到同一個(gè)ArrayList
對(duì)象中讯泣。且取出來時(shí)是Object類型纫普,需要強(qiáng)制類型轉(zhuǎn)換后才能進(jìn)行相應(yīng)的操作。但由于ArrayList
對(duì)象能接受任何類型好渠,無法保證類型轉(zhuǎn)換總是正確的昨稼,很容易造成ClassCastException異常节视。
??????但泛型的出現(xiàn),讓這一切都迎刃而解假栓。單個(gè)ArrayList
對(duì)象只能存儲(chǔ)特定類型的對(duì)象寻行,如果不是存入該類型或者該類型子類的對(duì)象,編譯器會(huì)報(bào)錯(cuò)提醒匾荆,規(guī)范了ArrayList
中對(duì)象的類型拌蜘。同時(shí),取出來時(shí)可以安心的依據(jù)泛型的具體類型進(jìn)行強(qiáng)制類型轉(zhuǎn)換牙丽,并且這是在ArrayList
中自動(dòng)完成強(qiáng)轉(zhuǎn)的拦坠,省去了開發(fā)者進(jìn)行強(qiáng)制類型轉(zhuǎn)換帶來的繁瑣。
#daqiJava.java
List<String> list = new ArrayList();
list.add("");
String str = list.get(0);
list.add(1);//編譯器不通過
總的來說剩岳,泛型帶來以下好處:
- 在編譯期檢查類型,讓類型更安全
- 自動(dòng)類型轉(zhuǎn)換
- 提高代碼通用性
類型參數(shù)約束
上界
類型參數(shù)約束可以限制作為泛型類和泛型函數(shù)的類型實(shí)參的類型入热。
??????把一個(gè)類型指定為泛型的類型形參的上界約束拍棕,在泛型類型具體的初始化中,對(duì)應(yīng)的類型實(shí)參必須是這個(gè)具體類型或它的子類型勺良。
??????換句話說就是绰播,某泛型函數(shù)(例如求和函數(shù))可以用在List<Int>
和List<Double>
上,但不可以用在List<String>
上尚困。這時(shí)可以指定泛型類型型參的上界為Number
,使類型參數(shù)必須使數(shù)字蠢箩。
fun <T:Number> sum(num1:T,num2:T):T{
}
一旦指定上界,只有 Number
的子類(子類型)可以替代 T事甜。
尖括號(hào)中只能指定一個(gè)上界谬泌,如果同一類型參數(shù)需要多個(gè)上界,需要使用 where-子句:
fun <T> daqi(list: List<T>)
where T : CharSequence,T : Comparable<T> {
}
類型形參非空
??????類型參數(shù)約束默認(rèn)的上界是 Any?
逻谦。意味著泛型函數(shù)接收的參數(shù)可空掌实,盡管泛型T并沒有標(biāo)記? 。這時(shí)可以使用<T : Any>
替換默認(rèn)上界,確保泛型T永遠(yuǎn)為非空類型。
類腹殿、類型 和 子類型
類 與 類型
??????學(xué)習(xí)泛型的型變之前蛤克,需要先學(xué)習(xí)本小節(jié)的內(nèi)容,以便更好的理解后面的泛型的型變伟件。在Java中,我們往往會(huì)把類和類型當(dāng)作相同的概念來使用,但其實(shí)它們是兩種不同概念父丰。區(qū)分類和類型這兩種概念的同時(shí),也需要分情況討論:
- 非泛型類
??????非泛型類的名稱可以直接當(dāng)作類型使用橱脸。而在Kotlin中础米,一個(gè)非泛型類至少可以分為兩種類型:非空類型和可空類型分苇。例如String
類,可以分為可空類型String?
和 非空類型String
.
- 泛型類
??????而對(duì)于泛型類就變得更為復(fù)雜了屁桑。一個(gè)泛型類想得到合法的類型医寿,必須用一個(gè)具體的類型作為泛型的類型形參。因此一個(gè)泛型類可以衍生出無限數(shù)量的類型蘑斧。例如:Kotlin的List
是一個(gè)類靖秩,不是一個(gè)類型。其合法類型:List<String>
竖瘾、List<Int>
等沟突。
子類 和 子類型
??????我們一般將一個(gè)類的派生類稱為子類,該類稱為父類(基類)捕传。例如:Int
是Number
的派生類,Number
作為父類惠拭,Int
作為子類。
而子類型與子類的定義不一樣,子類型的定義:
任何時(shí)候期望A類型的值時(shí)庸论,可以使用B類型的值,則B就是A的子類型
超類型是子類型的反義詞职辅。如果B是A的子類型,那么反過來A就是B的超類型聂示。
- Int是Number的子類型
??????對(duì)于非泛型類,其類型會(huì)沿襲該類的繼承關(guān)系域携,當(dāng)A是B的父類,同時(shí)A類型也是B類型的超類型鱼喉。當(dāng)期望A類型的對(duì)象時(shí)秀鞭,可以使用B類型的對(duì)象進(jìn)行傳遞。
- String是String?的子類型
??????所有類的 非空類型 都是該類的 可空類型 的子類型扛禽,但反過來不可以锋边。例如:在接收String?
類型的地方,可以使用String
類型的值來替換旋圆。但不能將String?
類型的值存儲(chǔ)到String
類型的值中宠默,因?yàn)?code>null不是非空類型變量可以接收的值。(除非進(jìn)行判空或非空斷言灵巧,編譯器將可空類型轉(zhuǎn)換為非空類型搀矫,這時(shí)原可空類型的值可以存儲(chǔ)到非空類型的變量中)
- Int不是String的子類型
??????作為非泛型類,Int
和String
沒有繼承關(guān)系刻肄,兩者間不存在子類型或超類型的關(guān)系瓤球。
為什么存在型變
??????我們都知道非泛型類其類型會(huì)沿襲該類的繼承關(guān)系。但對(duì)于泛型類敏弃,這是行不通的卦羡。例如以下代碼,是無法編譯成功的:
#daqiJava.java
List<String> strList = new ArrayList();
List<Object> objList = new ArrayList();
objList = strList;
??????List<Object>
和List<String>
是兩個(gè)相互獨(dú)立的類型,不存在子類型的關(guān)系绿饵。即便String
類的基類是Object
類欠肾。
??????因?yàn)楫?dāng)你期望List<Object>
時(shí),允許賦值一個(gè)List<String>
過來拟赊,也就意味著其他的類型(如List<Int>
等)也能賦值進(jìn)來刺桃。這就造成了類型不一致的可能性,無法確保類型安全吸祟,違背了泛型引入的初衷 —— 確保類型安全瑟慈。
??????到這里你或許會(huì)想,對(duì)于接收泛型類對(duì)象的方法屋匕,這不就"削了"泛型類的代碼通用性(靈活性)的能力葛碧?Java提供了有限制的通配符來確保類型安全,允許泛型類構(gòu)建相應(yīng)的子類型化關(guān)系过吻,提高代碼的通用性(靈活性)进泼。與之對(duì)應(yīng)的,便是Kotlin的型變纤虽。Kotlin中存在協(xié)變和逆變兩種概念缘琅,統(tǒng)稱為聲明處型變。
聲明處型變
??????Kotlin的聲明處型變包含了協(xié)變和逆變廓推。協(xié)變和逆變都是用于規(guī)范泛型的類型形參的范圍,確保類型安全翩隧。
協(xié)變
- 協(xié)變主要概念:
保留子類型化關(guān)系
具體意思是:當(dāng) B 是 A 的子類型樊展,那么List<B>
就是List<A>
的子類型。協(xié)變類保留了泛型的類型形參的子類型化關(guān)系堆生。
- 基本定義 使用out關(guān)鍵字
public fun Out(list: List<out String>) {
}
逆變
- 逆變主要概念:
反轉(zhuǎn)子類型化關(guān)系
具體意思是:當(dāng) B 是 A 的子類型专缠,那么List<A>
就是List<B>
的子類型。逆變類反轉(zhuǎn)了泛型的類型形參的子類型化關(guān)系淑仆。
- 基本定義 使用in關(guān)鍵字
public fun In(list: MutableList<in String>) {
}
圖解協(xié)變和逆變
??????對(duì)于協(xié)變的定義普遍很容易理解涝婉,但對(duì)于逆變往往比較費(fèi)解。所以我決定退一步蔗怠,借助Java的有限制的通配符進(jìn)行了解墩弯。從官方文檔中了解到,協(xié)變寞射、逆變和Java的通配符類型參數(shù)有以下關(guān)系:
- out A 對(duì)應(yīng)Java的通配符類型參數(shù)為:? extends A
??????通配符類型參數(shù) ? extends A 表示接受 A 或者 A 的子類型渔工。
- in A 對(duì)應(yīng)Java的通配符類型參數(shù)為:? super A
??????通配符類型參數(shù) ? super A 表示接受 A 或者 A 的超類型。
所以桥温,out Number
和 in Number
的"取值范圍"可以用一張圖概括(暫時(shí)只考慮由非泛型類的繼承帶來的子類型化關(guān)系):
??????Number
類具有Int
引矩、Long
等派生類,同時(shí)也擁有Any
這個(gè)基類。當(dāng)需要依據(jù)Number
進(jìn)行協(xié)變時(shí)(即<out Number>
)旺韭,泛型的類型形參只能選取Number
自身以及其子類(子類型)氛谜。當(dāng)需要依據(jù)Number
進(jìn)行逆變時(shí)(即<in Number>
),泛型的類型形參只能選取Number
自身以及其基類(超類型)区端。
??????當(dāng)某方法中需要List<out Number>
類型的參數(shù)時(shí)值漫,將<out Number>
轉(zhuǎn)換為<? extends Number>
,表示泛型的類型形參可以為Number
自身以及其子類(子類型)。即List<Number>
協(xié)變的子類型集合有:List<Number>
珊燎、List<Int>
等惭嚣。
??????List<Int>
在List<Number>
協(xié)變的子類型集合中。意味著當(dāng)需要List<Number>
時(shí)悔政,可以使用List<Int>
來替換晚吞,List<Int>
是List<Number>
的子類型。符合協(xié)變的要求: Int
是 Number
的子類型谋国,以致List<Int>
也是List<Number>
的子類型槽地。
??????而如果協(xié)變的是List<Int>
,那么將<out Int>
轉(zhuǎn)換為<? extends Int>
芦瘾。表示泛型的類型形參可以為Int
自身以及其子類(子類型)捌蚊。即List<Int>
協(xié)變的子類型集合只有:List<Int>
。
??????List<Number>
不在List<Int>
協(xié)變的子類型集合中近弟。意味著當(dāng)需要List<Int>
時(shí)缅糟,不可以使用List<Number>
來替換,List<Number>
不是List<Int>
的子類型祷愉。
??????這種思路對(duì)于逆變也是可行的窗宦。某方法中需要MutableList<in Number>
類型的參數(shù)時(shí),將<in Number>
轉(zhuǎn)換為<? super Number>
,表示泛型的類型形參可以為Number
自身以及其基類(超類型)二鳄。即MutableList<Number>
逆變的子類型集合有:MutableList<Number>
赴涵、MutableList<Any>
等。
??????MutableList<Int>
不在MutableList<Number>
逆變的子類型集合中订讼。意味著當(dāng)需要MutableList<Number>
時(shí)髓窜,不可以使用MutableList<Int>
來替換,MutableList<Int>
不是MutableList<Number>
的子類型欺殿。
??????而如果逆變的是MutableList<Int>
寄纵,那么將<in Int>
轉(zhuǎn)換為<? super Int>
。表示泛型的類型形參可以為Int
自身以及其基類(超類型)脖苏。即MutableList<Int>
逆變的子類型集合有:MutableList<Int>
擂啥、MutableList<Number>
和 MutableList<Any>
。
??????MutableList<Number>
在MutableList<Int>
逆變的子類型集合中帆阳。意味著當(dāng)需要MutableList<Int>
時(shí)哺壶,可以使用MutableList<Number>
來替換屋吨,MutableList<Number>
是MutableList<Int>
的子類型。符合逆變的要求: Int
是 Number
的子類型山宾,但MutableList<Number>
是MutableList<Int>
的子類型至扰。
可空類型與非空類型的聲明處型變
??????眾所周知,Kotlin中一個(gè)非泛型類有著對(duì)應(yīng)的可空類型和非空類型资锰,而且非空類型是可空類型的子類型敢课。因?yàn)楫?dāng)需要可空類型的對(duì)象時(shí),可以使用非空類型的對(duì)象來替換绷杜。
??????關(guān)于可空類型和非空類型間的協(xié)變與逆變直秆,也可以使用剛才的方法進(jìn)行理解,只是這次不再局限于子類和父類鞭盟,而是擴(kuò)展到子類型和超類型圾结。
- 當(dāng)需要依據(jù)類型A進(jìn)行協(xié)變時(shí)(即
<out A>
),泛型的類型形參只能選取A自身以及其子類型齿诉。 - 當(dāng)需要依據(jù)類型A進(jìn)行逆變時(shí)(即
<in A>
)筝野,泛型的類型形參只能選取A自身以及其超類型。
??????當(dāng)某方法中需要List<out Any?>
類型的參數(shù)時(shí)粤剧,將<out Any?>
轉(zhuǎn)換為<? extends Any?>
,表示泛型的類型形參可以為Any?
自身以及其子類型歇竟。即List<Any?>
協(xié)變的子類型集合有:List<Any?>
、List<Any>
等抵恋。
??????而如果逆變的是MutableList<Any?>
焕议,那么將<in Any?>
轉(zhuǎn)換為<? super Any?>
。表示泛型的類型形參可以為Any?
自身以及其超類型弧关。即MutableList<Any?>
逆變的子類型集合有:MutableList<Any?>
号坡。
??????當(dāng)你試圖將MutableList<Any>
做為子類型傳遞給接收MutableList<in Any?>
類型參數(shù)的方法時(shí),編譯器將報(bào)錯(cuò)梯醒,編譯不通過。因?yàn)?code>MutableList<Any?>逆變的子類型集合中沒有MutableList<Any>
腌紧。
??????當(dāng)某方法中需要List<out Any>
類型的參數(shù)時(shí)茸习,將<out Any>
轉(zhuǎn)換為<? extends Any>
,表示泛型的類型形參可以為Any
自身以及其子類型。即List<Any>
協(xié)變的子類型集合有:List<Any>
壁肋。
??????而如果逆變的是MutableList<Any>
号胚,那么將<in Any>
轉(zhuǎn)換為<? super Any>
。表示泛型的類型形參可以為Any
自身以及其超類型浸遗。即MutableList<Any>
逆變的子類型集合有:MutableList<Any>
和 MutableList<Any?>
猫胁。
??????當(dāng)你試圖將List<Any?>
做為子類型傳遞給接收List<out Any>
類型參數(shù)的方法時(shí),編譯器將報(bào)錯(cuò)跛锌,編譯不通過弃秆。因?yàn)?code>List<Any>協(xié)變的子類型集合中沒有List<Any?>
。
in位置 和 out位置
??????到這里或許有個(gè)疑問,我該依據(jù)什么來選擇協(xié)變或者逆變呢菠赚?這就涉及關(guān)鍵字out
和in
的第二層含義了脑豹。
關(guān)鍵字out的兩層含義:
- 子類型化被保留。
- T 只能用在out位置衡查。
關(guān)鍵in的兩層含義:
- 子類型化被反轉(zhuǎn)瘩欺。
- T 只能用在in位置。
?????? out位置是指:該函數(shù)生產(chǎn)類型為T
的值拌牲,泛型T只能作為函數(shù)的返回值俱饿。而in位置是指:該函數(shù)消費(fèi)類型T的值,泛型T作為函數(shù)的形參類型塌忽。
消費(fèi)者 和 生產(chǎn)者
??????Kotlin的型變遵從《Effective Java》中的 PECS (Producer-Extends, Consumer-Super)拍埠。只能讀取的對(duì)象作為生產(chǎn)者,只能寫入的對(duì)象作為消費(fèi)者砚婆。
-
out
關(guān)鍵字使得一個(gè)類型參數(shù)協(xié)變:只可以被生產(chǎn)而不可以被消費(fèi)械拍。
??????out
修飾符確保類型參數(shù) T 從 Iterator<T>
成員中返回(生產(chǎn)),并從不被消費(fèi)装盯。
public interface Iterator<out T> {
public operator fun next(): T
public operator fun hasNext(): Boolean
}
-
in
關(guān)鍵字使得一個(gè)類型參數(shù)逆變:只可以被消費(fèi)而不可以被生產(chǎn)坷虑。
??????in
修飾符確保類型參數(shù) T
從 Comparable<T>
成員中寫入(消費(fèi)),并從不被生產(chǎn)埂奈。
interface Comparable<in T> {
operator fun compareTo(other: T): Int
}
out位置
??????配合協(xié)變分析迄损,可以清楚out
為什么扮演生產(chǎn)者角色:
- 1、由于協(xié)變的關(guān)系账磺,
List<Int>
芹敌、List<Long>
等子類型可以替代List<Number>
,傳遞給接收List<Number>
類型的方法。而對(duì)外仍是List<Number>
垮抗,但并不知道該泛型類實(shí)際的類型形參是什么氏捞。 - 2、當(dāng)對(duì)其進(jìn)行寫入操作時(shí)冒版,可以接收
Number
的任何子類型液茎。但由于不知道該泛型類實(shí)際的類型形參是什么。對(duì)其進(jìn)行寫入會(huì)造成類型不安全辞嗡。(例如:可能接收的是一個(gè)List<Int>
捆等,如果你對(duì)其寫入一個(gè)Long
,這時(shí)就會(huì)造成類型不安全续室。) - 3栋烤、當(dāng)對(duì)其進(jìn)行讀取操作時(shí),不管它原本接收的是什么類型形參的泛型實(shí)例(不管是
List<Int>
,還是List<Long>
等)挺狰,返回(生產(chǎn))的是Number
實(shí)例明郭。以超類型的形式返回子類型實(shí)例买窟,類型安全。
in位置
??????配合逆變分析达址,也可以清楚in
為什么扮演消費(fèi)者角色:
- 1蔑祟、由于逆變的關(guān)系,
Consumer<Number>
沉唠、Consumer<Any>
等子類型可以替代Consumer<Number>
,傳遞給接收Consumer<Number>
類型的方法疆虚。 - 2、當(dāng)對(duì)其進(jìn)行寫入操作時(shí)满葛,可以接收Number的任何子類型径簿。不管接收的是Number的什么子類型,對(duì)外始終是
Consumer<Number>
(Consumer<Int>
嘀韧、Consumer<Long>
等不能傳遞進(jìn)來)篇亭。以超類型的形式消費(fèi)子類型實(shí)例,類型安全锄贷。 - 3译蒂、當(dāng)對(duì)其進(jìn)行讀取操作時(shí),由于不知道該泛型類實(shí)際的類型形參是什么(是
Number
呢谊却,還是Any
呢柔昼?)。只有使用Any
返回(生產(chǎn))才能確保類型安全炎辨,所以讀取受限捕透。(也就是說在逆變中,泛型T
為Number
時(shí)碴萧,你返回的不是Number
乙嘀,而是Any
。)
UnSafeVariance注解
??????那是否意味著out
關(guān)鍵字修飾的泛型參數(shù)是不是不能出現(xiàn)在in
位置 ?當(dāng)然不是破喻,只要函數(shù)內(nèi)部能保證不會(huì)對(duì)泛型參數(shù)存在寫操作的行為虎谢,可以使用UnSafeVariance
注解使編譯器停止警告,就可以將其放在in
位置曹质。out
關(guān)鍵字修飾的泛型參數(shù)也是同理婴噩。
??????例如Kotlin的List
中contains
函數(shù)等,就是應(yīng)用UnSafeVariance
注解使泛型參數(shù)存在于in位置咆繁,其內(nèi)部沒有寫操作。
public interface List<out E> : Collection<E> {
override val size: Int
override fun isEmpty(): Boolean
override fun contains(element: @UnsafeVariance E): Boolean
override fun iterator(): Iterator<E>
override fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean
public operator fun get(index: Int): E
public fun indexOf(element: @UnsafeVariance E): Int
public fun lastIndexOf(element: @UnsafeVariance E): Int
public fun listIterator(): ListIterator<E>
public fun listIterator(index: Int): ListIterator<E>
public fun subList(fromIndex: Int, toIndex: Int): List<E>
}
其他
??????構(gòu)造方法的參數(shù)既不在in
位置也不在out
位置顶籽。同時(shí)該位置規(guī)則只對(duì)對(duì)外公開的API有效玩般。(即對(duì)private
修飾的函數(shù)無效)
聲明處型變總結(jié)
協(xié)變 | 逆變 | 不變 | |
---|---|---|---|
結(jié)構(gòu) | Producer<out T> |
Consumer<in T> |
MutableList<T> |
Java實(shí)現(xiàn) | Producer<? extends T> |
Consumer<? super T> |
MutableList<T> |
子類型化關(guān)系 | 保留子類型化關(guān)系 | 逆轉(zhuǎn)子類型化關(guān)系 | 無子類型化關(guān)系 |
位置 | out位置 | in位置 | in位置和out位置 |
角色 | 生產(chǎn)者 | 消費(fèi)者 | 生產(chǎn)者和消費(fèi)者 |
表現(xiàn) | 只讀 | 只寫,讀取受限 | 即可讀也可寫 |
選擇逆變礼饱、協(xié)變和不變
那么使用泛型時(shí)坏为,逆變究驴、協(xié)變和不變?nèi)绾芜x擇呢?
- 首先需要考慮泛型形參的位置:只讀操作(協(xié)變或不變)匀伏、只寫讀操作(逆變或不變)洒忧、又讀又寫操作(不變)。
??????Array中存在又讀又寫的操作够颠,如果為其指定協(xié)變或逆變熙侍,都會(huì)造成類型不安全:
class Array<T>(val size: Int) {
fun get(index: Int): T { …… }
fun set(index: Int, value: T) { …… }
}
- 最后判斷是否需要子類型化關(guān)系,子類型化關(guān)系主要用于提高API的靈活度履磨。
??????如果需要子類型化關(guān)系蛉抓,則只讀操作(協(xié)變或不變)選擇協(xié)變,否則不變;只寫讀操作(逆變或不變),選擇逆變剃诅,否則不變巷送。
星點(diǎn)投射
??????Kotlin的型變分為 聲明處型變 和 星點(diǎn)投射。所謂的星點(diǎn)投射就是使用 * 代替類型參數(shù)矛辕。表示你不知道關(guān)于泛型實(shí)參的任何信息笑跛,但仍然希望以安全的方式使用它。
Kotlin 為此提供了以下星點(diǎn)投射的語(yǔ)法:
對(duì)于
Foo <T : TUpper>
聊品,其中 T 是一個(gè)具有上界TUpper
的不型變類型參數(shù)飞蹂,Foo<*>
讀取值時(shí)等價(jià)于Foo<out TUpper>
,而寫值時(shí)等價(jià)于Foo<in Nothing>
杨刨。對(duì)于
Foo <out T : TUpper>
晤柄,其中 T 是一個(gè)具有上界TUpper
的協(xié)變類型參數(shù),Foo <*>
等價(jià)于Foo <out TUpper>
妖胀。 這意味著當(dāng)T
未知時(shí)芥颈,你可以安全地從Foo <*>
讀取TUpper
的值。對(duì)于
Foo <out T>
赚抡,其中 T 是一個(gè)協(xié)變類型參數(shù)爬坑,Foo <*>
等價(jià)于Foo <out Any?>
。 因?yàn)?T
未知時(shí)涂臣,只有讀取Any?
類型的元素是安全的盾计。對(duì)于
Foo <in T>
,其中T
是一個(gè)逆變類型參數(shù)赁遗,Foo <*>
等價(jià)于Foo <in Nothing>
署辉。 因?yàn)?T
未知時(shí),沒有什么可以以安全的方式寫入Foo <*>
岩四。對(duì)于普通的
Foo <T>
哭尝,這其中沒有任何泛型實(shí)參的信息。Foo<*>
讀取值時(shí)等價(jià)于Foo<out Any?>
,因?yàn)樽x取Any?
類型的元素是安全的剖煌;Foo<*>
寫入值是等價(jià)于Foo<in Nothing>
材鹦。
??????如果泛型類型具有多個(gè)類型參數(shù)逝淹,則每個(gè)類型參數(shù)都可以單獨(dú)投影(以interface Function <in T, out U>
為例):
- Function<*, String> 表示 Function<in Nothing, String>。
- Function<Int, *> 表示 Function<Int, out Any?>桶唐。
- Function<*, *> 表示 Function<in Nothing, out Any?>栅葡。
MutableList<*>和MutableList<Any?>的區(qū)別
??????可以向MutableList<Any?>
中添加任何數(shù)據(jù),但MutableList<*>只是通配某種類型尤泽,因?yàn)椴恢榔渚唧w什么類型欣簇,所以不允許向該列表中添加元素,否則會(huì)造成類型不安全安吁。
參考資料:
- 《Kotlin實(shí)戰(zhàn)》
- Kotlin官網(wǎng)
android Kotlin系列:
Kotlin知識(shí)歸納(一) —— 基礎(chǔ)語(yǔ)法
Kotlin知識(shí)歸納(二) —— 讓函數(shù)更好調(diào)用
Kotlin知識(shí)歸納(三) —— 頂層成員與擴(kuò)展
Kotlin知識(shí)歸納(六) —— 類型系統(tǒng)
Kotlin知識(shí)歸納(十一) —— 高階函數(shù)