Kotlin中 ?、!!、?:、:: 、->、== 符號的簡單說明

Kotlin 中新增了一些符號,先記錄在案,方便理解和學習。

1. ? 和 !!

"?"加在變量名後,系統在任何情況不會報它的空指針異常。
"!!"加在變量名後,如果對象爲null,那麼系統一定會報異常!

上述是兩個符號的簡單概念,爲了更好的解釋這兩個概念,我們先從java代碼入手,如下例:

ArrayList<String> myList = null;     //  創建一個null的隊列
Log.d("TAG", "-->> List Size = " + myList.size());

這個例子中,執行到 Log 打印隊列長度時,報錯

 “  Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.util.ArrayList.size()' on a null object reference”

然而在 KT 中,在調用 myList 的時候在它後面加上一個問號 myList?.size() ,則會在 myList 爲null 的時候直接打印出null ,不會拋出 NullPointerException 。
在Android Studio 中,將上述代碼自動轉換爲 KT代碼寫法後,如下:

val myList: ArrayList<String>? = null     //  創建一個null的隊列
Log.d("TAG", "-->> List Size = " + myList!!.size)

編譯器爲什麼自動將 myList.size() 變成了 myList!!.size ,而不是加上 ? 呢?
這是因爲編譯器在轉換時爲了保證代碼轉換前後的一致性所造成的。換言之,在Java 上出異常的,轉換到 KT 上,編譯器依然會讓它保持拋出異常,NullPointerException 也是如此。
綜上所述,使用 ?時,程序會進行非空判斷,如爲空,則返回 null ,不會造成程序報錯。但注意返回的是 null 而非 0 ,如果用於判斷,參考 2中 ?:的講解
使用 !! 時,會對對象進行非空判斷,並且會像 java 代碼一樣拋出異常。

2. ?:

對象A ?: 對象B 表達式,意思爲,當對象 A值爲 null 時,那麼它就會返回後面的對象 B。

val roomList: ArrayList<KTBean>? = null
if (roomList?.size > 0) {
	Log.d("TAG", "-->> 列表數不是0")
}

如上段代碼,執行時會發現如下提示
在這裏插入圖片描述
此時,就可以使用上述講到的 ?: ,當 ?: 前面的對象爲空時,返回後面的值,如下:

val roomList: ArrayList<Room>? = null
val mySize= roomList?.size ?: 0  

此時,mySize 的值就爲0,因爲 roomList?.size 爲空,可用於列表是否爲空的判斷,如下:

val roomList: ArrayList<KTBean>? = null
if (roomList?.size ?: 0 > 0) {    // 這一行添加了?:
	Log.d("TAG", "-->> 列表數不是0")
}

至此,用上面的 ?和 ?: 就可以避免程序中出現的 NullPointerException 。
參考鏈接:https://www.jianshu.com/p/51b2e5aa3dd8

3. ::

Kotlin 中 雙冒號操作符 表示把一個方法當做一個參數,傳遞到另一個方法中進行使用,通俗的來講就是引用一個方法。
詳情參考:https://blog.csdn.net/lv_fq/article/details/72869124

4. ->

fun <T, R> Collection<T>.fold(
    initial: R, 
    combine: (acc: R, nextElement: T) -> R
): R {
    var accumulator: R = initial
    for (element: T in this) {
        accumulator = combine(accumulator, element)
    }
    return accumulator
}

在上述代碼中,參數 combine 具有函數類型 (R, T) -> R,因此 fold 接受一個函數作爲參數, 該函數接受類型分別爲 R 與 T 的兩個參數並返回一個 R 類型的值。 在 for-循環內部調用該函數,然後將其返回值賦值給 accumulator。

5. == 和 ===

code 1

fun main(args: Array<String>) {
	val a : Int = 1000
	println(a == a) //true
	println(a === a) //true
	val a1 : Int = a
	val a2 : Int = a
	println(a1 == a2) //true
	println(a1 === a2) //true
}

code 2

fun main(args: Array<String>) {
	val a : Int = 1000
	println(a == a) //true
	println(a === a) //true
	val a1 : Int? = a
	val a2 : Int? = a
	println(a1 == a2) //true
	println(a1 === a2) //false
}

在Kotlin中,=== 表示比較對象地址,== 表示比較兩個值大小。
所以無論是 a == a 還是 a === a 都是返回true,因爲是同一個變量,數值大小和地址都是相等的。

現在重點看 a1 和 a2,這裏的把 a 分別賦給 a1 和 a2。
code 1 和 code 2 的不同點在於 a1 和 a2 的類型。一個是Int,一個是Int?。它們的區別如下:

如果我們使用的是 val a : Int = 999 這種方式,這時的a其實就是個數值, 不涉及裝箱的問題, 也就不是對象。
而如果我們使用的是 val a: Int? = 999 這種方式,這時的a是一個Int型對象, 因爲它涉及到裝箱問題。
code 1 中 a1 和 a2 都沒有裝箱,所以不是對象,只是數值,所以數值大小和地址都是相等的。而 code 2 中 a1 和 a2 涉及到裝箱,已經變成了對象,此時它們的數值仍然相等,但地址已經不同了(因爲是不同對象)。
code 3

fun main(args: Array<String>) {
	val a : Int? = 1000
	println(a == a) //true
	println(a === a) //true
	val a1 : Int? = a
	val a2 : Int? = a
	println(a1 == a2) //true
	println(a1 === a2) //true
}

code 3 和 code 2 做比較,發現將a也裝箱後,a1 === a2返回 true,這是爲什麼呢?

因爲這裏的a經過裝箱後本身已經一個對象,所以賦給a1和a2的時候是把直接把對象a賦給它們,所以此時a1和a2指的是同一個對象(對象a)。既然是同一個對象,那麼數值大小和地址肯定都是相等的了(也就是說a,a1和a2這三個對象指向同一處地址,所以其實 a === a1和a === a2也是返回true)。
code 4

fun main(args: Array<String>) {
	val a : Int = 100
	println(a == a) //true
	println(a === a) //true
	val a1 : Int? = a
	val a2 : Int? = a
	println(a1 == a2) //true
	println(a1 === a2) //true
}

code 4 和 code 2 做比較,明明只是改了一下a的值,爲什麼就會產生不同的結果呢?

這裏跟 Java 中是一樣的,在範圍是 [-128, 127] 之間的數裝箱時並不會創建新的對象,所以這裏a1和a2裝箱後的對象是同一個,a1 === a2也就返回true了。這裏改爲128或-129就又會變成false了。
參考鏈接:https://blog.csdn.net/fzhhsa/article/details/83278321

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章