Kotlin中的泛型

Kt中的泛型是一大特色盈蛮!和Java不太相似,或者說是補(bǔ)齊了Java的坑技矮!

簡單的使用泛型抖誉,不再贅述!

在Java中衰倦,使用泛型的時候袒炉,可以使用 extend 關(guān)鍵字表示當(dāng)前的泛型必須是某個類的子類!才能正常使用樊零! Kt中當(dāng)然也支持這樣的方法我磁! kt中叫上界孽文!

// 這里我們指定了,T的上界是Number ! T 實(shí)際的類型夺艰,必須是 Number 或者 Number的子類芋哭!
fun <T : Number> List<T>.sum() : T
15449441136560.jpg

上界還有一個作用!就是指定泛型為不可空類型郁副!如果你的上界不是一個可空類型减牺,那么你的泛型就不能被替換成可空的類型!

類型擦除

在Java中我們都知道 泛型在運(yùn)行時是被擦除的存谎!在Kt中也是如此的烹植,在運(yùn)行時候,我們沒辦法區(qū)別 List<Int>List<String>

但是 kt 中提供了一種有局限的解決方法愕贡!通過inlinereified兩個關(guān)鍵字草雕,來支持真泛型函數(shù)!但是也是有局限的固以!

// 我們 使用了內(nèi)聯(lián) 和真泛型
inline fun <reified T> isInstance(obj: Any?): Boolean {
    if (obj == null) return false
    return obj is T
}

fun main(args: Array<String>) {

    val obj = "kt"
    val isString = isInstance<String>(obj)
    println(isString)

}

這里的泛型可以做如下的事情:

  1. 在類型檢查和類型轉(zhuǎn)換中使用 (is , !is , as , as?
  2. 可以獲取到 KClass , 通過 T::class可以獲取KClass墩虹,當(dāng)然獲取了Kt的KClass,也就能相應(yīng)的獲取到 Java 的 Class對象了
  3. 可以作為調(diào)用其他泛型函數(shù)的憨琳,類型實(shí)參

不能做以下的事情:

  1. 創(chuàng)建類型參數(shù)對應(yīng)的類的實(shí)例
  2. 調(diào)用類型的伴生對象的方法
  3. 把類诫钓,屬性,和非內(nèi)聯(lián)的設(shè)置成reified

協(xié)變逆變

Kt中支持泛型的變型的篙螟!可以使用 outin 來定義變型的方式菌湃!out表示協(xié)變,in表示逆變遍略!

類和類型

什么是類惧所!什么是類型!如果在沒有泛型的類中绪杏,這很簡單下愈,一個類就是代表一種類型!但是在存在泛型的時候蕾久,我們就需要用另外的方式思考類型了势似!每一個含有泛型聲明的類!都是一個類型構(gòu)造器僧著!當(dāng)傳入不同的類型的泛型的時候履因,會構(gòu)造成不同的類型!List 接口是一個有一個泛型的類型構(gòu)造器盹愚!List<Int>栅迄,List<String>他們是通過List泛型構(gòu)造器構(gòu)造出來的類型!所以有泛型的類杯拐!可以構(gòu)造出無數(shù)種類型霞篡!只要他們的泛型不同,就是不同的類型6吮啤(沒有泛型的類朗兵,也能看成是含有0個泛型的類型構(gòu)造器!)

通過泛型構(gòu)造的類型顶滩,之間的繼承關(guān)系如何呢余掖?比如List<Any>List<Int>是什么關(guān)系呢?如果我們直接定義成礁鲁,兩者之間沒有任何的關(guān)系(Java就是這么做的)盐欺,有的時候我們會發(fā)現(xiàn),程序很難描述仅醇!

15449622081620.jpg

上圖中冗美,我們定義了 feedAll 方法 !它能喂養(yǎng)Animal群析二,但是我們傳入一個 Cat 群的時候粉洼,發(fā)現(xiàn)編譯器是報錯的!為什么叶摄?因?yàn)?code>Herd<Animal>和Herd<Cat>之間沒有任何的關(guān)系属韧,沒辦法將一個 Herd<Cat>類型的變量傳遞給Herd<Animal>類型!這很不可思議對吧蛤吓!所以說我們需要一種工具描述泛型構(gòu)造器構(gòu)造出來的類型之間的關(guān)系宵喂!
這就是我們要說的型變

型變分 協(xié)變逆變

  1. 協(xié)變:如果 A 是 B 的父類会傲,并且通過泛型構(gòu)造器 L 锅棕,構(gòu)造的 L<A>L<B> 的父類!那么就說L是協(xié)變的淌山!
  2. 逆變:通過泛型構(gòu)造器L哲戚,L<A>L<B> 的子類,就說L是逆變的艾岂!

型變的理論只發(fā)生在有泛型的類型構(gòu)造器上顺少!

我們上面的 Herd<Cat> 應(yīng)該是 Herd<Animal>的子類才是!說我們應(yīng)該講 Herd類設(shè)置成 協(xié)變! class Herd<out T : Animal>

但是我們不能將所有的泛型都設(shè)置成out 或者 in王浴,這兩者也不是亂用的脆炎!在kt中他們只能放在特定的地方!

將一個泛型設(shè)置成 outin 會影響這個泛型的使用的位置氓辣!被out修飾的泛型只能被放在out位置秒裕!被in修飾的只能放在in位置!

15449628978179.jpg

out位置我們指的是生產(chǎn)者位置钞啸!我們不能消費(fèi)它几蜻!

in位置我們指的是消費(fèi)者位置喇潘,我們不能生產(chǎn)它!

如果違背了這樣的規(guī)則梭稚,實(shí)際上類型是會出現(xiàn)問題的颖低!但是在kt中編譯器能幫助你正確的使用inout,用錯了會在編譯時報錯弧烤!

out 用在消費(fèi)者上

舉例上面的 Transformer 接口忱屑,如果此時 Tout的(協(xié)變的)!現(xiàn)有 A暇昂,B類莺戒,其中類A是類B的父類,記做A <= B!
Transformer 是 協(xié)變的急波!則 Transformer<A> <= Transformer<B>从铲,以下代碼:

val a : Transformer<A> 
val b : Transformer<B> = Transformer<B>()

// 根據(jù)里氏替換原則,子類可以代替父類澄暮!即 b 賦值給 a
a = b 

a.transform(/* 傳入的可以是 A 或者A的子類對象 */)

// 但是 最終 transform 是通過多態(tài)調(diào)用到 b 對象的食店!b對象的 transform方法只能接受B或者B的子類對象!這樣就沖突了赏寇!

如果 Transformer 是逆變的呢吉嫩?

Transformer<B> <= Transformer<A>

val a : Transformer<A> = Transformer<A>()

// 里氏替換原則!
val b : Transformer<B> = a 

b.transform( /* 能傳入B或者B的子類 */)

// 方法也是通過多態(tài)嗅定,最終調(diào)用的是 a 對象的 `transform` 方法! 這個方法自娩,只能接受 A 或者 A的子類!我們傳入的是B或者B的子類渠退,他們也必然是A的子類忙迁,所以沒有問題!

// 但是又會出現(xiàn)新的問題碎乃!

val res : B = b.transform( /* someObj */)

// 我們調(diào)用 b 的 transform 方法姊扔!從接口層次看,我們能拿到一個 B對象梅誓,或者B對象的子類對象恰梢!但是這個方法是最終多態(tài)調(diào)用a的,a的transform 方法梗掰,返回的 A或者A的子類對象嵌言!此時我們就是把 A對象賦值給B類型了,違背了里氏替換原則<八搿(父類對象不能代替子類對象4蒈睢)

從上面的分析可以看得出,參數(shù)和返回值是不能用同一種型變的埂陆!參數(shù)是逆變的苛白,返回值是協(xié)變的娃豹!所以我們分出了 inout位置,位置上的泛型购裙,不能亂用懂版!

15449485369323.jpg

使用點(diǎn)變型

kt中我們通過在泛型類上使用 intout關(guān)鍵字,聲明泛型的型變方式缓窜!這種聲明型變的方式我們稱為 聲明點(diǎn)變型,在Java中我們也有型變谍咆!例如我們可以使用List<? extends Object> 來接一個 List<String>的對象禾锤!這種叫做使用點(diǎn)變型

使用點(diǎn)泛型需要在使用泛型的地方摹察,都要去使用操作符恩掷!比較的麻煩!kt這種聲明式更加的簡單供嚎!

在kt中我們也支持使用點(diǎn)泛型黄娘,主要的作用是,給那些克滴,沒有聲明型變方式的類提供的1普(Java中的泛型類,都是不型變的劝赔,kt支持使用點(diǎn)泛型也是為了兼容性考慮J慕埂),你可以給沒有聲明型變方式的類着帽,在調(diào)用點(diǎn)設(shè)置inout表示型變杂伟!你不可以在使用的地方聲明和類中型變方式相反的型變類型!例如kt中 List是協(xié)變的 仍翰,你不可以什么一個List<in T>類型赫粥!編譯器會報錯的!但是你可以聲明List<out T>類型予借!不會報錯越平,但是是多此一舉的。

如果你在參數(shù)中灵迫,使用了使用點(diǎn)泛型那么你也會限制這些對象的使用場景喧笔!

例如 MutableList 是不型變的:


val list: MutableList<in String> = MutableList<String>()

// 你不能用 String去接!只能用 Any? 去接!因?yàn)樗撬蓄惖母割悾?val i : String = alis[1]

val list2: MutableList<out String> = MutableList<String>()
// 編譯出錯龟再!你不能給協(xié)變的List书闸,添加任何的東西!因?yàn)槿雲(yún)⑹窃?`in` 位置利凑!
list2.add("hello")

我們說上面的 listlist2 不是一個常規(guī)的MutableList浆劲,它們被稱為投影嫌术!受限的MutableList

使用類型投影,會導(dǎo)致對象的方法調(diào)用受限制牌借!

星(*)投影

如果你不知道關(guān)于類型參數(shù)的任何信息度气!那么你可以使用 * 表示他們!

星投影使用在不同的地方膨报,有不同的含義磷籍!

  1. Function<*, String> 表示 Function<in Nothing, String>
  2. Function<Int, *> 表示 Function<Int, out Any?>现柠;
  3. Function<*, *> 表示 Function<in Nothing, out Any?>

星投影院领,只能使用在,你對泛型具體類型不感興趣的地方够吩!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末比然,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子周循,更是在濱河造成了極大的恐慌强法,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件湾笛,死亡現(xiàn)場離奇詭異饮怯,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)嚎研,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進(jìn)店門硕淑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人嘉赎,你說我怎么就攤上這事置媳。” “怎么了公条?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵拇囊,是天一觀的道長。 經(jīng)常有香客問我靶橱,道長寥袭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任关霸,我火速辦了婚禮传黄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘队寇。我一直安慰自己膘掰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著识埋,像睡著了一般凡伊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上窒舟,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天系忙,我揣著相機(jī)與錄音,去河邊找鬼惠豺。 笑死银还,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的洁墙。 我是一名探鬼主播蛹疯,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼扫俺!你這毒婦竟也來了苍苞?” 一聲冷哼從身側(cè)響起固翰,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤狼纬,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后骂际,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疗琉,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年歉铝,在試婚紗的時候發(fā)現(xiàn)自己被綠了盈简。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡太示,死狀恐怖柠贤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情类缤,我是刑警寧澤臼勉,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站餐弱,受9級特大地震影響宴霸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜膏蚓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一瓢谢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧驮瞧,春花似錦氓扛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽破停。三九已至,卻和暖如春尉剩,著一層夾襖步出監(jiān)牢的瞬間真慢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工理茎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留黑界,地道東北人。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓皂林,卻偏偏與公主長得像朗鸠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子础倍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容