Kotlin知識(shí)歸納(十二) —— 泛型

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è)具體類型或它的子類型勺良。

image

??????換句話說就是绰播,某泛型函數(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è)類的派生類稱為子類,該類稱為父類(基類)捕传。例如:IntNumber的派生類,Number作為父類惠拭,Int作為子類。

而子類型與子類的定義不一樣,子類型的定義:

任何時(shí)候期望A類型的值時(shí)庸论,可以使用B類型的值,則B就是A的子類型

超類型是子類型的反義詞职辅。如果B是A的子類型,那么反過來A就是B的超類型聂示。

image
  • 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的子類型

??????作為非泛型類,IntString沒有繼承關(guān)系刻肄,兩者間不存在子類型或超類型的關(guān)系瓤球。

image

為什么存在型變

??????我們都知道非泛型類其類型會(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 Numberin Number的"取值范圍"可以用一張圖概括(暫時(shí)只考慮由非泛型類的繼承帶來的子類型化關(guān)系):

image

??????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>等惭嚣。

image

??????List<Int>List<Number>協(xié)變的子類型集合中。意味著當(dāng)需要List<Number>時(shí)悔政,可以使用List<Int>來替換晚吞,List<Int>List<Number>的子類型。符合協(xié)變的要求: IntNumber 的子類型谋国,以致List<Int>也是List<Number>的子類型槽地。

??????而如果協(xié)變的是List<Int>,那么將<out Int>轉(zhuǎn)換為<? extends Int>芦瘾。表示泛型的類型形參可以為Int自身以及其子類(子類型)捌蚊。即List<Int>協(xié)變的子類型集合只有:List<Int>

image

??????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>等。

image

??????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>

image

??????MutableList<Number>MutableList<Int>逆變的子類型集合中帆阳。意味著當(dāng)需要MutableList<Int>時(shí)哺壶,可以使用MutableList<Number>來替換屋吨,MutableList<Number>MutableList<Int>的子類型。符合逆變的要求: IntNumber 的子類型山宾,但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?>号坡。

image

??????當(dāng)你試圖將MutableList<Any>做為子類型傳遞給接收MutableList<in Any?>類型參數(shù)的方法時(shí),編譯器將報(bào)錯(cuò)梯醒,編譯不通過。因?yàn)?code>MutableList<Any?>逆變的子類型集合中沒有MutableList<Any>腌紧。

image

??????當(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?>猫胁。

image

??????當(dāng)你試圖將List<Any?>做為子類型傳遞給接收List<out Any>類型參數(shù)的方法時(shí),編譯器將報(bào)錯(cuò)跛锌,編譯不通過弃秆。因?yàn)?code>List<Any>協(xié)變的子類型集合中沒有List<Any?>

image

in位置 和 out位置

??????到這里或許有個(gè)疑問,我該依據(jù)什么來選擇協(xié)變或者逆變呢菠赚?這就涉及關(guān)鍵字outin的第二層含義了脑豹。

關(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ù)的形參類型塌忽。

image

消費(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ù) TComparable<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í)例买窟,類型安全。
image

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))才能確保類型安全炎辨,所以讀取受限捕透。(也就是說在逆變中,泛型 TNumber時(shí)碴萧,你返回的不是Number乙嘀,而是Any。)
image

UnSafeVariance注解

??????那是否意味著out關(guān)鍵字修飾的泛型參數(shù)是不是不能出現(xiàn)在in位置 ?當(dāng)然不是破喻,只要函數(shù)內(nèi)部能保證不會(huì)對(duì)泛型參數(shù)存在寫操作的行為虎谢,可以使用UnSafeVariance注解使編譯器停止警告,就可以將其放在in位置曹质。out關(guān)鍵字修飾的泛型參數(shù)也是同理婴噩。

??????例如Kotlin的Listcontains函數(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ì)造成類型不安全安吁。

參考資料:

android Kotlin系列:

Kotlin知識(shí)歸納(一) —— 基礎(chǔ)語(yǔ)法

Kotlin知識(shí)歸納(二) —— 讓函數(shù)更好調(diào)用

Kotlin知識(shí)歸納(三) —— 頂層成員與擴(kuò)展

Kotlin知識(shí)歸納(四) —— 接口和類

Kotlin知識(shí)歸納(五) —— Lambda

Kotlin知識(shí)歸納(六) —— 類型系統(tǒng)

Kotlin知識(shí)歸納(七) —— 集合

Kotlin知識(shí)歸納(八) —— 序列

Kotlin知識(shí)歸納(九) —— 約定

Kotlin知識(shí)歸納(十) —— 委托

Kotlin知識(shí)歸納(十一) —— 高階函數(shù)

Kotlin知識(shí)歸納(十二) —— 泛型

Kotlin知識(shí)歸納(十三) —— 注解

Kotlin知識(shí)歸納(十四) —— 反射

image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末醉蚁,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鬼店,更是在濱河造成了極大的恐慌网棍,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妇智,死亡現(xiàn)場(chǎng)離奇詭異滥玷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)巍棱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門惑畴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人航徙,你說我怎么就攤上這事如贷。” “怎么了到踏?”我有些...
    開封第一講書人閱讀 165,830評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵杠袱,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我窝稿,道長(zhǎng)楣富,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評(píng)論 1 295
  • 正文 為了忘掉前任伴榔,我火速辦了婚禮纹蝴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘踪少。我一直安慰自己塘安,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評(píng)論 6 393
  • 文/花漫 我一把揭開白布援奢。 她就那樣靜靜地躺著兼犯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上免都,一...
    開封第一講書人閱讀 51,754評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音帆竹,去河邊找鬼绕娘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛栽连,可吹牛的內(nèi)容都是我干的险领。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼秒紧,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼绢陌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起熔恢,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤脐湾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后叙淌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秤掌,經(jīng)...
    沈念sama閱讀 45,847評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評(píng)論 3 338
  • 正文 我和宋清朗相戀三年鹰霍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了闻鉴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,137評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡茂洒,死狀恐怖孟岛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情督勺,我是刑警寧澤渠羞,帶...
    沈念sama閱讀 35,819評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站玷氏,受9級(jí)特大地震影響堵未,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜盏触,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評(píng)論 3 331
  • 文/蒙蒙 一渗蟹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赞辩,春花似錦雌芽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至糟需,卻和暖如春屉佳,著一層夾襖步出監(jiān)牢的瞬間谷朝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工武花, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留圆凰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,409評(píng)論 3 373
  • 正文 我出身青樓体箕,卻偏偏與公主長(zhǎng)得像专钉,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子累铅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評(píng)論 2 355