Kotlin 空類(lèi)型安全與智能類(lèi)型轉(zhuǎn)換
一、前言
在 Kotlin 中的類(lèi)與接口?中我們已經(jīng)講了 Kotlin 的?類(lèi)、接口?和?擴(kuò)展方法,今天我們來(lái)講 Kotlin 中的?空類(lèi)型安全?和?智能類(lèi)型轉(zhuǎn)換。
二、Kotlin 空類(lèi)型安全
2.1、空類(lèi)型安全概念
Java 語(yǔ)言中是沒(méi)有空類(lèi)型安全這一概念的,所以寫(xiě) Java 代碼經(jīng)常會(huì)出現(xiàn)空指針異常,但是 Kotlin 致力于消除空引用所帶來(lái)的危險(xiǎn),所以就有了空類(lèi)型安全概念。
下面這段代碼在 Kotlin 中是無(wú)法編譯通過(guò)的,因?yàn)?Kotlin 的 String 是不能接受空值的,所以這個(gè)賦值操作是不被允許的。
var?nonNull:?String?=?"Hello"
nonNull?=?null??//?不可空類(lèi)型,不能賦值為?null
//?訪問(wèn)長(zhǎng)度的話,不需要空判斷
val?length?=?nonNull.length
IDE 報(bào)錯(cuò)如下所示:

但是如果我們非得定義一個(gè)空值,也是有辦法的,Kotlin 為了 100% 兼容 Java,必須得實(shí)現(xiàn)接收空值,所以要接收空值可以在定義的時(shí)候加一個(gè) ?。
但是這個(gè)時(shí)候這個(gè)變量就可能是空值,所以訪問(wèn)的時(shí)候就會(huì)比較嚴(yán)格,比如下面代碼中的 nullable.length 就會(huì)報(bào)錯(cuò),因?yàn)榭赡苡|發(fā)空指針異常。
var?nullable:?String??=?"Hello"
nullable?=?null??//?可空類(lèi)型,編譯通過(guò)
val?length?=?nullable.length?//?可能觸發(fā)空指針,編譯保報(bào)錯(cuò)
IDE 報(bào)錯(cuò)如下所示:

這個(gè)時(shí)候我們想 nullable.length 不報(bào)錯(cuò)可以:
當(dāng)我們確定 nullable 不可能為空的時(shí)候,可以將 nullable 強(qiáng)轉(zhuǎn)為不可空類(lèi)型,如下所示: var?nullable:?String??=?"Hello"
//?不會(huì)報(bào)錯(cuò)了(自己已經(jīng)知道了,這個(gè)?nullable?不可能為空)
val?length?=?nullable!!.length如果我們不確定 nullable 是否為空的時(shí)候,可以用 ?. 來(lái)實(shí)現(xiàn)安全訪問(wèn),如下所示: var?nullable:?String??=?"Hello"
//?這種情況下如果?nullable?為空的話,那么返回的?length?也是空
val?length?=?nullable?.length
//?這時(shí)候?length?的類(lèi)型不是?Int,而是?Int?,所以可以寫(xiě)成下面這樣
val?length:?Int??=?nullable?.length
//?如果你想?length?的類(lèi)型是?Int?的話,可以加個(gè)默認(rèn)值,寫(xiě)成下面這樣
val?length:?Int?=?nullable?.length??:?0??//?如果?nullable?.length?為空,?就返回0
2.2、空類(lèi)型的繼承關(guān)系
我們知道 Int 是 Number 的子類(lèi),所以通過(guò)下面的代碼我們就可以知道 String 應(yīng)該是 String? 的子類(lèi)。
var?a:?Int?=?2
var?b:?Number?=?10.0
a?=?b?//?Type?mismatch,報(bào)錯(cuò)
b?=?a?//?OK
var?x:?String?=?"Hello"
var?y:?String??=?"World"
x?=?y?//?Type?mismatch,報(bào)錯(cuò)
y?=?x?//?OK
2.3、Kotlin 空類(lèi)型安全回顧

三、Kotlin 智能類(lèi)型轉(zhuǎn)換
3.1、智能類(lèi)型轉(zhuǎn)換例子
//?定義一個(gè)接口
public?interface?Kotliner?{
}
//?定義一個(gè)類(lèi)?Person?實(shí)現(xiàn)?Kotliner?接口
public?class?Person?implements?Kotliner?{
????public?final?String?name;
????public?final?int?age;
????public?Person(String?name,?int?age)?{
????????this.name?=?name;
????????this.age?=?age;
????}
????public?static?void?main(String[]?args)?{
????????//?用子類(lèi)的實(shí)例賦值給接口的引用
????????Kotliner?kotliner?=?new?Person("Test",?20);
????????//?這里已經(jīng)判斷了是不是?Person,但是下面還是要強(qiáng)制類(lèi)型轉(zhuǎn)換
????????if?(kotliner?instanceof?Person)?{
????????????System.out.println(kotliner.name);??//?這樣寫(xiě)報(bào)錯(cuò)
????????????System.out.println(((Person)?kotliner).name);??//?正確寫(xiě)法
????????}
????}
}
val?kotliner:?Kotliner?=?Person("Test",?20)
if?(kotliner?is?Person)?{
????println((kotliner?as?Person).name)??//?不需要強(qiáng)轉(zhuǎn),可以智能轉(zhuǎn)換,所以下面的寫(xiě)法就可以了
????println(kotliner.name)
}
下面這段代碼定義了一個(gè) value, 類(lèi)型是 String?,if 判斷其不為空,所以在 if 判斷這個(gè)括號(hào)里面,value 的類(lèi)型會(huì)被智能轉(zhuǎn)換成 String,當(dāng)然出了括號(hào),value 的類(lèi)型就又是 String? 了。
var?value:?String??=?null
value?=?"Test"
if?(value?!=?null)?{
????//?value:?String???==>??String
????println(value.length)
}
//?value:?String?
...
3.2、不支持智能類(lèi)型轉(zhuǎn)換的情況
下面這種情況就不支持智能類(lèi)型轉(zhuǎn)換,因?yàn)檫@個(gè)公共變量很多地方都可以訪問(wèn),所以我們?cè)谂袛嗖粸榭罩螅锌赡軇e的線程又把 tag 改成了空,所以這種情況智能類(lèi)型轉(zhuǎn)換就失效了。
//?在?main?函數(shù)之外定義一個(gè)公共變量
var?tag:?String??=?null
fun?main()?{
????if(tag?!=?null)?{
????????//?有可能被改成空
????????println(tag.length)??//?報(bào)錯(cuò),不支持智能類(lèi)型轉(zhuǎn)換
????}
}
3.3、幾個(gè)建議
盡可能使用 val 來(lái)聲明不可變引用,讓程序的含義更加清晰確定; 盡可能減少函數(shù)對(duì)外部變量的訪問(wèn); 必要的時(shí)候曾創(chuàng)建局部變量指向外部變量,避免因它變化引起程序錯(cuò)誤。
3.4、Kotlin 智能類(lèi)型轉(zhuǎn)換

四、小結(jié)
五、源碼

評(píng)論
圖片
表情
