<本文學習郭神《第三行代碼》總結>
1、可空類型
Kotlin在編譯時,會有自己的判空機制,可以幾乎杜絕空指針異常
首先,寫一段代碼:
fun setDate(a: Test) {
a.getText()
a.getContent()
}
這段代碼沒有空指針風險,並且這段代碼無法傳入空參數。
因爲Kotlin默認所有參數不可爲空,所以這裏傳入的參數也是不可空的。如果這裏傳入一個null參數,則會提示錯誤:Null can not bea value of a non-null type Study,也就是說,Kotlin在將空指針異常的檢查提前到了編譯期。
如果開發過程中,業務邏輯需要使用null參數,則需要進行可空類型操作。
可空類型很簡單,就是在類名後加一各問好,比如:
Int | 表示不可空整型 |
---|---|
Int? | 表示可空整型 |
String | 表示不可控字符串 |
String? | 表示可空字符串 |
那麼,將上述代碼修改爲
fun setDate(a: Test?) {
a.getText()
a.getContent()
}
就可以傳入null參數,就不會再提示錯誤。
但是,傳入了空參數,會導致方法裏面的調用方法getText()、getContent()出現空指針異常,無法通過Kotlin編譯,所以必須要進行判空處理。
fun setDate(a: Test?) {
if (a != null) {
a.getText()
a.getContent()
}
}
2、判空輔助工具
(1)?. 操作符
如果需要判空處理過多,則會寫很多if,導致很繁瑣,而且if也無法處理全局變量,所以這裏需要使用輔助工具。
比如,對以下代碼進行判空處理進行簡化
if (a != null) {
a.getText()
}
簡化成
a?.getText()
這裏?.就是判空操作符,所以上面的代碼就可以寫成
fun setDate(a: Test?) {
a?.getText()
a?.getContent()
}
這樣,通過使用?.操作符就可替換if
(2)?: 操作符
這個操作符左右兩邊都接收一個表達式,如果左邊表達式結果不爲空,則返回左邊,否則,返回右邊。
val c = if (a != null){
a
} else{
b
}
可以簡化成
val c = a ?: b
(3)兩種操作符共同使用
比如一段代碼
fun getData(content : String?) : Int{
if (content != null)
return content.length
return 0
}
可以簡化成
fun getData(content : String?) = content?.length ?: 0
(4)!! 操作符
Kotlin判空機制也存在漏洞,因爲有時候從邏輯上已經判空處理,但是編譯器並不知道,還是會編譯失敗。
var content: String? = "aa"
fun main(){
if (content != null){
val text = content.toUpperCase()
}
}
定義一個全局變量content,在main方法中進行判空處理,當不爲空時執行下一步操作。
但是這段代碼無法編譯運行,因爲toUpperCase()並不知道外部已經對content進行了判空處理,所以在調用時,還是認爲存在空指針風險。
在這種情況下,要想通過編譯,只能使用 !! ,進行強制通過。
var content: String? = "aa"
fun main(){
if (content != null){
val text = content!!.toUpperCase()
}
}
這是一種風險寫法,因爲這是在強制進行通過編譯,如果在開發中,出現null情況,則程序會崩潰。
(5)let
let不是操作符,也不是關鍵字,而是函數。這個函數提供了函數式的API編程接口,並將調用對象作爲參數傳遞到Lambda表達式中。
obj.let { obj2 ->
//函數體
}
這裏,調用了obj對象的let函數,然後Lambda表達式中的代碼就會立即執行,並且這個obj對象本身還會作爲參數傳遞到Lambda表達式中,這裏,爲了防止重名,將obj參數名改成了obj2,實際是同一個對象。
fun setDate(a: Test?) {
a?.getText()
a?.getContent()
}
這段代碼通過?. 操作符可以正常編譯,如果將這段換成if判斷,則是:
fun setDate(a: Test?) {
if (a != null){
a?.getText()
}
if (a != null){
a?.getContent()
}
}
也就是說,使用?. 每次調用方法,都會進行一次if 判斷,這樣會增加邏輯運算,不簡潔。所以還可以簡化成
fun setDate(a: Test?) {
a?.let { a1 ->
a1.getText()
a1.getContent()
}
?. 操作符表示對象爲空,什麼都不做,對象不爲空就調用let函數,而let函數會將a 對象本身作爲參數傳遞到Lambda表達式中,此時a 對象肯定不爲空。
當Lambda參數只有一個時,可以直接用it關鍵字代替,所以這裏可以簡化成:
fun setDate(a: Test?) {
a?.let {
it.getText()
it.getContent()
}
此外,let函數還可以進行全局變量的判空問題處理,而使用if則不行,使用if對全局變量判空則需要使用!!進行強制編譯通過,使用let不會出現這種情況。