空值Null Safety

      在〈空值Null Safety〉中尚無留言

寫過Java專案的人,一定常苦腦於程式閃退問題。一查log,靠北喔,怎麼死於 NullPointerException(NPE)。明明就已經考慮清楚,此段不可能NPE啊,怎麼查都找不到原因。花了很多時間才查到原來是使用者沒有正常操作或是一些莫名奇妙的問題所產生。

可空型別(Nullable)與不可空型別(Non-null)

Kotlin為了消除Java的苦腦而重新設計。kotlin預設變數都是不可有空值,這樣就不會有NPE了。

等等,怪怪的喔!! 這跟怕駭客入侵網路,就把網路線拔掉有啥區別? 又等等,真的有人會作這種蠢事嗎? 有的,台灣所有的政府單位就是這麼搞的。

當然,kotlin絕對不可能這麼吃屎。因為它只是預設不能有空值,但也可以手動改成有空值。也就是在變數型態後面加上 “?”。

fun main(args:Array<String>) {
var a:Int=0
a=null//此行錯誤,因為a不可以有空值

var b:Int?=0
b=null//b允許有空值
}

空值處理

當變數宣告為可空值時,下面紅面部份無法編譯,會出現
Only safe(?.) or non-null asserted(!!.) calls are allowed on a nullable receiver of type Int?

fun main(args:Array<String>) {
var a:Int?=null
var f=a.toFloat()//此行會出現錯誤無法編譯
}

正確的處理方式是在轉換之前,要先用if判斷一下a是否為null,如下所示

fun main(args:Array<String>) {
var a:Int?=null
var f=if (a!=null)a.toFloat()
else 0.0f

安全運算子(Safe Calls operators)

哇塞,為了解決NPE,搞到最後都要先用 if 判斷是否空值,這不就違背了易讀簡化的原則了嗎?

還好kotlin開發團隊也不是省油的燈,發明了安全運算子 “?.”。
當a不是null時,就進行轉換,若a為null,則傳回null

fun main(args:Array<String>) {
var a:Int?=null
println(a?.toFloat())
}
結果:
null

當然也可以用變數接收轉換的值,只是接收的變數必需宣告為如下

var b:Float?=a?.toFloat()
或簡化為
var b=a?toFloat()

標準函數let

a?.let{} 其中的let稱為標準函數,當a不為null時,就執行let{}區塊裏的程式碼。區塊內的 “it” 變數,譯成中文為 “它”,就是指 a 這個變數的意思。

let函數用 if 也可達成。不論是用 if 還是 let,這都讓程式碼變得複雜,所以就盡量不要使用。

fun main(args:Array<String>) {
var a:Int?=10
var b=a?.let {
it.toFloat()
}
println(b)
}

鏈式呼叫(Chain)

如下類別Node, 又有left及right都可能是null, 所以主程式裏就可以寫成
node?.left?.d

此例必需有類別基礎才能看懂,所以可以待研讀類別後,再回來理解。

fun main(args:Array<String>) {
var node:Node?=null
println(node?.left?.d)
}
class Node{
var left:Node?=null
var d:Int=0
var right:Node?=null
}

Elvis

Elvis[ˋɛlvɪs]中文翻成艾維斯,也是貓王的稱呼。 kotlin的 elvis 符號是 “?:” 。那麼 “?:” 又跟貓王有啥關係? 網路上是說 “?” 像貓的頭髮,”:”是貓王的眼睛。其實外國人唬爛的功力也不比中國人差。

先看一下代碼

fun main(args:Array<String>) {
var a:Int?=null
var b:Float?=a?.toFloat()
println("b is $b")

var c:Float=a?.toFloat()?:0.0f
println("c is $c")
}
結果:
b is null
c is 0.0

變數 var b:Float?=a?.toFloat(),b為Float, 也可能是null

變數 var c:Float=a?.toFloat() ?: 0.0f,c一定是Float~~~
a? 若a 不為null ,進行轉換。
?: 若a 為 null,傳回 0.0

如何? 說穿了,也沒啥鳥不起,對吧。

非空斷言(Not-null assertion operator)

assertion[ə’sə:ʃən] 這個字是武斷的意思,也就是自以為是的意思。白話來說,就是~~應該是這個樣子。在電腦裏,可以解釋為~~應該是,但又不確定。

Not-null assertion 有人譯成” 非空斷言”,這是一個很好的翻譯名詞,因為斷言不代表一定。

“!!.” 為非空斷言運算子,比如 a!!.toFloat()。當 a不為null時,進行轉換,若 a 為 null,則拋出KotlinNullPointerException。

當程式設計師100%確定變數不會是null時,可以加上 a!!.toFloat()。這等於是跟編譯器說,林北確定不會有null產生,出事的話林北負責。

那麼這不就又回到Java時代,常常讓程式閃退嗎。是的,所以盡可能不要用。但有時又不得不用,所以才會有這個非空斷言運算子。

fun main(args:Array<String>) {
var a:Int?=null
println(a!!.toFloat())
}

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *