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

前序

??????Kotlin引入可空性的新特性钙态,旨在消除來(lái)自代碼空引用的危險(xiǎn)。將運(yùn)行時(shí)的NPE轉(zhuǎn)變成編譯器的錯(cuò)誤菇晃。

可空類型與非空類型

??????在Kotlin類型系統(tǒng)中册倒,分為可空類型和非空類型。當(dāng)你允許一個(gè)變量為null時(shí)磺送,需要顯示在類型后面加上一個(gè)問(wèn)號(hào)驻子,將其非空類型轉(zhuǎn)換為可空類型。

??????常見(jiàn)的類型都是非空類型估灿,不能存儲(chǔ)null引用崇呵,只有在類型后面添加個(gè)問(wèn)號(hào)轉(zhuǎn)換為可空類型后,變量才可存儲(chǔ)null引用馅袁。

val str:String? = null

??????對(duì)于一個(gè)可空類型的值域慷,不能直接調(diào)用該類型的方法,也不能把他賦值給非空類型,更不能把它傳遞給接受非空類型參數(shù)的函數(shù)犹褒⌒址模可空類型看似和非空類型并沒(méi)有什么交互性,但其實(shí)并不是化漆,只是需要對(duì)可空類型進(jìn)行一個(gè)判空后,才能正常交互:

val str:String? = “”
if (str != null)
    str.length

??????一旦對(duì)可空類型的對(duì)象進(jìn)行判空钦奋,編譯器就會(huì)對(duì)判空的作用域內(nèi)把該對(duì)象當(dāng)作非空對(duì)待座云。

Kotlin的可空性與Java的Optional

??????Java8中引入的特殊包裝類型Optional來(lái)解決null引用問(wèn)題。但這種方法使代碼更加冗長(zhǎng)付材,并且額外的包裝類還影響運(yùn)行時(shí)的性能朦拖,因此并沒(méi)有被廣泛使用起來(lái)。

??????但在Kotlin中厌衔,可空和非空的對(duì)象在運(yùn)行時(shí)沒(méi)有什么區(qū)別璧帝,可空類型并不是非空類型的包裝類。所有的檢查都是編譯器完成富寿,這使得Kotlin的可空類型并不會(huì)在運(yùn)行時(shí)帶來(lái)額外的開(kāi)銷睬隶。

安全調(diào)用運(yùn)算符:?.

??????Kotlin標(biāo)準(zhǔn)庫(kù)中有一個(gè)高效的安全調(diào)度運(yùn)算符:?. 。它將null檢查和調(diào)用合并成一個(gè)操作页徐。當(dāng)你使用?.調(diào)用一個(gè)可空類型對(duì)象的方法時(shí)苏潜,若值不為空,則方法會(huì)被正常執(zhí)行变勇;若值為null恤左,則方法調(diào)用不發(fā)生,并整個(gè)表達(dá)式返回null搀绣。

image

安全調(diào)用除了可以調(diào)用方法飞袋,還可以用來(lái)訪問(wèn)屬性。

Elvis運(yùn)算符:?:

??????Elvis運(yùn)算符?:用來(lái)提供替代null的默認(rèn)值链患。Elvis運(yùn)算符接收兩個(gè)表達(dá)式巧鸭,如果左側(cè)表達(dá)式非空,則返回其左側(cè)表達(dá)式锣险。當(dāng)左側(cè)表達(dá)式為空蹄皱,則返回右側(cè)表達(dá)式。

Elvis運(yùn)算符經(jīng)常與安全調(diào)度運(yùn)算符一起使用:

val str:String? = null
println(str?.length ?: 0)

Elvis運(yùn)算符也可以配合return 和 throw一起使用芯肤,當(dāng)運(yùn)算符左邊為null時(shí)巷折,能提前返回函數(shù)或拋出異常。

val str:String? = null
//為空拋一次
val length = str?.length ?: throw IllegalArgumentException()
println(length)
str?.let {
    println(length)
} ?: return

//等價(jià)于
if(str == null)
    //函數(shù)類型為空時(shí)直接打斷函數(shù)繼續(xù)執(zhí)行
    return
    
//str不為null,則繼續(xù)執(zhí)行崖咨。
println(length)

也可以配合run函數(shù)配合使用锻拘,替代if-lese:

str?.let { 
    //str不為空的邏輯
} ?: run { 
    //str為空時(shí)邏輯
}

非空斷言:!!

??????Kotlin為NPE愛(ài)好者提供非空斷言運(yùn)算符 !! (雙感嘆號(hào)),可以把任何對(duì)象轉(zhuǎn)換成非空類型,從而調(diào)用該對(duì)象方法署拟,但可能造成拋出NPE婉宰。

val str:String? = null
//拋NPE
println(str!!.length)

??????所以只有確保該可空類型對(duì)象不為空時(shí),才使用非空斷言推穷。當(dāng)使用非空斷言而且發(fā)生異常時(shí)心包,異常棧只表明異常發(fā)生在哪一行,并不會(huì)指明哪個(gè)表達(dá)式馒铃,所以最好避免同一行中使用非空斷言蟹腾。

安全轉(zhuǎn)換:as?

??????和常規(guī)的Java轉(zhuǎn)換一樣,當(dāng)被轉(zhuǎn)換的值不是你視圖轉(zhuǎn)換的類型時(shí)区宇,會(huì)拋出ClassCastException異常娃殖。一般解決方案是在使用在轉(zhuǎn)換前使用is檢查來(lái)確定該值是否符合轉(zhuǎn)換類型。但Kotlin提供更簡(jiǎn)潔的運(yùn)算符——安全轉(zhuǎn)換運(yùn)算符:as?

//定義父類和子類
open class Animal{
    fun getName(){
    }
}
class Dog:Animal(){
    fun getDogName(){
    }
}

fun main(args:Array<String>){
    val animal:Animal = Dog()
    val dog = animal as? Dog ?: return
    dog.getDogName()
}

安全轉(zhuǎn)換運(yùn)算符嘗試將值轉(zhuǎn)換成給定的類型议谷,否則返回null:

image

let函數(shù)

??????let函數(shù)將調(diào)用它的對(duì)象變成lambda表達(dá)式的參數(shù)炉爆。配合安全調(diào)度運(yùn)算符可以把調(diào)用let函數(shù)的可空對(duì)象,轉(zhuǎn)變成非空類型卧晓。然后在let函數(shù)中調(diào)用一系列對(duì)該可空類型的操作芬首。

fun main(args:Array<String>){
    val str:String? = null
    str?.let {
        daqi(it)
    }
}

fun daqi(str:String){

}

??????當(dāng)需要檢查多個(gè)值是否為null時(shí),不建議使用嵌套的let調(diào)用來(lái)處理逼裆,建議使用一個(gè)if語(yǔ)句對(duì)這些值進(jìn)行一次性檢查衩辟。

可空類型的擴(kuò)展

??????對(duì)可空類型的進(jìn)行擴(kuò)展的好處是,允許接收者為null時(shí)調(diào)用擴(kuò)展函數(shù)波附,并在擴(kuò)展函數(shù)中處理null,而不用確保變量不為null后再調(diào)用該對(duì)象的方法艺晴。因?yàn)楫?dāng)實(shí)例為null時(shí),成員方法永遠(yuǎn)不會(huì)被執(zhí)行掸屡。

??????Kotlin標(biāo)準(zhǔn)庫(kù)中的CharSequence存在兩個(gè)擴(kuò)展函數(shù):isNullOrEmpty和isNullOrBlank封寞,可以由String?類型的接收者調(diào)用。

??????對(duì)可空類型定義擴(kuò)展函數(shù)時(shí)仅财,意味著函數(shù)體中的this可能為空狈究,需要做對(duì)應(yīng)的空處理。

fun String?.daqi(){
    if (this == null){
        println("this is null")
    }
}

fun main(args:Array<String>){
    val str:String? = null
    由于接收的是可空類型盏求,不需要使用?.
    str.daqi()
}

延遲初始化

??????Kotlin中抖锥,屬性聲明為非空類型時(shí),必須在構(gòu)造函數(shù)中初始化碎罚。但屬性可以在一個(gè)特殊的方法中磅废,通過(guò)依賴注入來(lái)初始化。這時(shí)不能在構(gòu)造函數(shù)中為屬性提供一個(gè)非空初始化器荆烈,但你仍想將該類型聲明為非空類型拯勉,避免空檢查竟趾。可以使用lateinit關(guān)鍵字修飾該變量宫峦,請(qǐng)將該變量使用var修飾岔帽,因?yàn)関al必須會(huì)編譯成必須在構(gòu)造方法中初始化的final字段。

class daqi{
    private lateinit var name:String
    
    fun onCreate(){
        name = "daqi"
    }
}

可空性與Java

??????Kotlin會(huì)根據(jù)Java中的可空性注解导绷,來(lái)對(duì)來(lái)自Java的類型分為可空類型和非空類型犀勒。如,@Nullable注解的對(duì)象妥曲,會(huì)被Kotlin當(dāng)作可空類型的對(duì)象账蓉。@Notnull注解的對(duì)象,會(huì)被Kotlin當(dāng)作非空類型的對(duì)象逾一。

??????當(dāng)可空性注解不存在時(shí),Java類型會(huì)被轉(zhuǎn)換為Kotlin的平臺(tái)類型肮雨。平臺(tái)類型本質(zhì)上是Kotlin不知道其可空信息遵堵,既可以把它當(dāng)作可空類型,也可以把它當(dāng)作非空類型怨规。如果選擇非空類型陌宿,編譯器會(huì)在賦值時(shí)觸發(fā)一個(gè)斷言,防止Kotlin的非空變量保存空值波丰。這意味著需要開(kāi)發(fā)者負(fù)責(zé)正確處理來(lái)自Java的值壳坪。

??????Kotlin定義的函數(shù)中,編譯器會(huì)生成對(duì)每個(gè)非空類型的參數(shù)的檢查掰烟,如果使用不正確的參數(shù)調(diào)用爽蝴,會(huì)立即拋出異常。(這種檢查在函數(shù)調(diào)用的時(shí)候就被執(zhí)行了纫骑,而不是等到該異常參數(shù)被使用時(shí)才執(zhí)行蝎亚。)

基本數(shù)據(jù)類型

??????Java區(qū)分基本數(shù)據(jù)類型和引用類型,基本數(shù)據(jù)類型具有高效存儲(chǔ)和傳遞的性質(zhì)先馆。當(dāng)你需要在泛型類中存儲(chǔ)一些基本數(shù)據(jù)類型時(shí)发框,需要以基本數(shù)據(jù)類型的包裝類型進(jìn)行存儲(chǔ)。因?yàn)镴VM不支持用基本數(shù)據(jù)類型作為類型參數(shù)煤墙。

??????Kotlin并不區(qū)分基本類型和包裝類型梅惯。對(duì)于變量、屬性和返回類型仿野,Kotlin的基本數(shù)據(jù)類型會(huì)被編譯成Java的基礎(chǔ)數(shù)據(jù)類型铣减。只有對(duì)于泛型類時(shí),才會(huì)被編譯器成對(duì)應(yīng)的Java基本類型包裝類脚作。

基本數(shù)據(jù)類型

??????當(dāng)使用Java聲明的基本數(shù)據(jù)類型變量時(shí)徙歼,該類型會(huì)變成非空類型,而不是平臺(tái)類型。因?yàn)镴ava的基本數(shù)據(jù)類型不能存儲(chǔ)null值魄梯。

??????Kotlin中可空的基本數(shù)據(jù)類型會(huì)被編譯成對(duì)應(yīng)的包裝類型桨螺,因?yàn)镴ava的基本數(shù)據(jù)類型不能存儲(chǔ)null值。

數(shù)字轉(zhuǎn)換

??????kotlin不會(huì)自動(dòng)把數(shù)字從一種類型轉(zhuǎn)換成另一種取值范圍更大的類型酿秸。Kotlin為每種基本數(shù)據(jù)類型(Boolean除外)都定義了轉(zhuǎn)換到其他基本數(shù)據(jù)類型的函數(shù)灭翔。

??????Kotlin要求轉(zhuǎn)換必須顯式的,因?yàn)樵贘ava中辣苏,比較裝箱值時(shí)肝箱,不僅檢查他們存儲(chǔ)的值,還會(huì)比較裝箱類型稀蟋。

//此處比較會(huì)返回false
new Integer(42).equals(new Long(42))

??????Kotlin標(biāo)準(zhǔn)庫(kù)為字符串也提供了轉(zhuǎn)換為基本數(shù)據(jù)類型的擴(kuò)展函數(shù)煌张。如果對(duì)字符串解析失敗,則拋出NumberFormatException()方法退客。

根類型

??????Any類型是所有Kotlin非空類型的超類骏融。但Any不能持有null值,當(dāng)需要持有任何值的變量包括null值萌狂,必須使用Any?

??????Any只包含toString档玻、equals和hashCode。所有Kotlin的這些方法都是從Any中繼承來(lái)得茫藏。但Any不能使用使用其他Object的方法(如:wait和notify)

類型參數(shù)的可空性

??????Kotlin中所以泛型類和泛型函數(shù)的類型參數(shù)默認(rèn)都是可空的误趴,因?yàn)槟J(rèn)上界是Any?

??????如果需要類型參數(shù)非空,則必須為其指定一個(gè)非空的上界:

fun <T:Any> daqi(t:T){
    
}

參考資料:

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í)歸納(十四) —— 反射

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末务傲,一起剝皮案震驚了整個(gè)濱河市凉当,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌售葡,老刑警劉巖纤怒,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異天通,居然都是意外死亡泊窘,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)像寒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)烘豹,“玉大人,你說(shuō)我怎么就攤上這事诺祸⌒酰” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵筷笨,是天一觀的道長(zhǎng)憔鬼。 經(jīng)常有香客問(wèn)我龟劲,道長(zhǎng),這世上最難降的妖魔是什么轴或? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任昌跌,我火速辦了婚禮,結(jié)果婚禮上照雁,老公的妹妹穿的比我還像新娘蚕愤。我一直安慰自己,他們只是感情好饺蚊,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布萍诱。 她就那樣靜靜地躺著,像睡著了一般污呼。 火紅的嫁衣襯著肌膚如雪裕坊。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,125評(píng)論 1 297
  • 那天燕酷,我揣著相機(jī)與錄音籍凝,去河邊找鬼。 笑死悟狱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的堰氓。 我是一名探鬼主播挤渐,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼双絮!你這毒婦竟也來(lái)了浴麻?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤囤攀,失蹤者是張志新(化名)和其女友劉穎软免,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體焚挠,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡膏萧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蝌衔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榛泛。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖噩斟,靈堂內(nèi)的尸體忽然破棺而出曹锨,到底是詐尸還是另有隱情,我是刑警寧澤剃允,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布沛简,位于F島的核電站齐鲤,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏椒楣。R本人自食惡果不足惜给郊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望撒顿。 院中可真熱鬧丑罪,春花似錦、人聲如沸凤壁。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)拧抖。三九已至煤搜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間唧席,已是汗流浹背擦盾。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留淌哟,地道東北人迹卢。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像徒仓,于是被迫代替她去往敵國(guó)和親腐碱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353