Kotlin可空性探索

目錄介紹

  • 01.可空性
  • 02.安全調用運算符:?.
  • 03.Elvis運算符:?:
  • 04.安全轉換運算符:as?
  • 05.非空斷言:!!
  • 06.let函數說明
  • 07.可空類型的擴展
  • 08.Java中判斷方式
  • 09.kotlin是否解決NPE
  • 10.kotlin如何約束非空判斷
  • 11.致使NPE的場景

01.可空性

  • 在 Kotlin 中,類型系統區分一個引用是能夠容納 null (可空引用)仍是不能容納(非空引 用)。 例如,String 類型的常規變量不能指向 null
    var name: String = "yc"
    //編譯錯誤
    //name = null
    複製代碼
  • 若是但願一個變量能夠儲存 null 引用,須要顯式地在類型名稱後面加上問好來標記它:
    var name: String? = "yc"
    name = null
    複製代碼
  • 問號能夠加在任何類型的後面來表示這個類型的變量能夠存儲 null 引用:Int?、Doubld? 、Long? 等
  • Kotlin 對可空類型的顯式支持有助於防止 NullPointerException 致使的異常問題,編譯器不容許不對可空變量作 null 檢查就直接調用其屬性
    fun check(name: String?): Boolean {
        //編譯器不容許不對 name 作 null 檢查就直接調用其屬性
        return name.isNotEmpty()
    }
    複製代碼
  • 須要顯式地進行 null 檢查
    fun check(name: String?): Boolean {
        if (name != null) {
            return name.isNotEmpty()
        }
        return false
    }
    複製代碼

02.安全調用運算符:?.

  • 安全調用運算符:?.
    • 容許把一次 null 檢查和一次方法調用合併爲一個操做,若是變量值非空,則方法或屬性會被調用,不然直接返回 null
  • 例如,如下兩種寫法是徹底等同的:
    fun check(name: String?) {
        if (name != null) {
            println(name.toUpperCase())
        } else {
            println(null)
        }
    }
    
    fun check(name: String?) {
        println(name?.toUpperCase())
    }
    複製代碼

03.Elvis運算符:?:

  • Elvis 運算符:?:
    • 用於替代 ?. 直接返回默認值 null 的狀況,Elvis 運算符接收兩個運算數,若是第一個運算數不爲 null ,運算結果就是其運算結果值,若是第一個運算數爲 null ,運算結果就是第二個運算數
  • 例如,如下兩種寫法是徹底等同的:
    fun check(name: String?) {
        if (name != null) {
            println(name)
        } else {
            println("yc")
        }
    }
    
    fun check(name: String?) {
        println(name ?: "yc")
    }
    複製代碼

04.安全轉換運算符:as?

  • 安全轉換運算符:as? 用於把值轉換爲指定的類型,若是值不適合該類型則返回 null
    fun check(any: Any?) {
        val result = any as? String
        println(result ?: println("is not String"))
    }
    複製代碼

05.非空斷言:!!

  • 非空斷言用於把任何值轉換爲非空類型,若是對 null 值作非空斷言,則會拋出異常
    fun main(args: Array<String>) {
        var name: String? = "yc"
        check(name) //7
    
        name = null
        check(name) //KotlinNullPointerException
    }
    
    fun check(name: String?) {
        println(name!!.length)
    }
    複製代碼

06.let函數說明

  • let 函數可用於在表達式不爲 null 時才執行指定代碼塊
    fun main(args: Array<String>) {
        var name: String? = "yc"
        check(name) //yc
    
        name = null
        check(name) //什麼都不會輸出
    }
    
    fun check(name: String?) {
        name?.let {
            println(name)
        }
    }
    複製代碼

07.可空類型的擴展

  • 爲可空類型定義擴展函數是一種更強大的處理 null 值的方式,能夠容許接收者爲 null 的調用,並在該函數中處理 null ,而不是在確保變量不爲 null 以後再調用它的方法
    • 例如,以下方法能夠被正常調用而不會發生空指針異常
    val name: String? = null
    println(name.isNullOrEmpty()) //true
    複製代碼
    • isNullOrEmpty() 的方法簽名以下所示,能夠看到這是爲可空類型 CharSequence? 定義的擴展函數,方法中已經處理了方法調用者爲 null 的狀況
    @kotlin.internal.InlineOnly
    public inline fun CharSequence?.isNullOrEmpty(): Boolean {
        contract {
            returns(false) implies (this@isNullOrEmpty != null)
        }
    
        return this == null || this.length == 0
    }
    複製代碼

08.Java中判斷方式

8.1 防護式編程

  • 「防護式編程」你們應該不陌生,核心思想是不信任任何「外部」輸入。
    • 不論是真實的用戶輸入仍是其餘模塊傳入的實參,具體點就是各類判空。建立一個方法須要判空,建立一個邏輯塊須要判空,甚至本身的代碼內部也須要判空(防止對象的回收之類的)。
    public void showToast(Activity activity) {
       if (activity == null) {
           return;
       }
    }
    複製代碼

8.2 契約式編程

  • 各個模塊之間約定好一種規則,你們按照規則來辦事,出了問題找沒有遵照規則的人負責,這樣能夠避免大量的判空邏輯。Android 提供了相關的註解以及最基礎的檢查來協助開發者,示例以下:博客
    public void showToast(@NonNull Activity activity) {
       ......
    }
    複製代碼
  • 給 Activity 增長了 @NonNull 的註解,就是向全部調用這個方法的人聲明瞭一個約定,調用方應該保證傳入的 activity 非空。固然聰明的你應該知道,這是一個很弱的限制,調用方沒注意或者不理會這個註解的話,程序就依然還有 NPE 致使的 crash 的風險。

09.kotlin是否解決NPE

  • 有些文章說 Kotlin 幫開發者解決了NPE(NullPointerException),這個說法是不對的。Kotlin沒有幫開發者解決了NPE,而是經過在語言層面增長各類強規則,強制開發者去本身處理可能的空指針問題,達到儘可能減小(只能減小而沒法徹底避免)出現 NPE 的目的。

10.kotlin如何約束非空判斷

  • 聲明階段
    • 變量須要決定本身是否可爲空,好比 private var goodsId: String ?= null,這樣就是可接受 null
  • 傳遞階段
    • 在變量傳遞階段,必須保持「可空性」一致,好比形參聲明是不爲空的,那麼實參必須自己是非空或者轉爲非空才能正常傳遞。示例以下:
    private var goodsId: String? = null
    private fun Main(){
        getName(goodsId!!)
    }
    
    private fun getName(name : String){
        Log.i("", "---$name")
    }
    複製代碼
    • 還有一種方式,在方法中添加?
    private fun Main(){
        getName(goodsId)
    }
    
    private fun getName(name : String?){
        Log.i("", "---$name")
    }
    複製代碼
  • 使用階段
    • 在使用階段,須要嚴格判空
    var time: Long? = 1000
    
    private fun Main(){
        time!!.toFloat()
        time?.toInt()
    }
    複製代碼
  • 得出結論
    • 總的來講 Kotlin 爲了解決 NPE 作了大量語言層級的強限制,的確能夠作到減小 NPE 的發生。但這種既「契約式」(判空)又「防護式」(聲明空與非空)的方案會讓開發者作更多的工做,會更「麻煩」一點。

11.致使NPE的場景

11.1 方法參數聲明非空

  • 好比,請求網絡接口,須要傳遞參數,這種狀況下每一個字段均可能爲空,爲了加強邏輯,能夠在方法參數上加上"?",能夠避免後端處理參數值時拋出空指針異常。博客
    /**
     * 用戶登錄
     */
    @POST("user/login")
    fun userLogin(
            @Query("username") userName: String?,
            @Query("password") password: String?
    ): Observable<ResponseBean<LoginBean>>
    複製代碼

11.2 !! 強行轉爲非空

  • 當將可空類型賦值給非空類型時,須要有對空類型的判斷,確保非空才能賦值(Kotlin 的約束)。使用!! 能夠很方便得將「可空」轉爲「非空」,但可空變量值爲 null,則會 crash。
    • 所以使用上建議在確保非空時才用 !!: param!!
    • 不然仍是儘可能放在判空代碼塊裏:
    param?.let {
       doSomething(it)
    }
    複製代碼

想換個工做,渴望同行內推我

  • 我的信息
    • 姓名:楊充【26歲】
    • 郵箱:yangchong211@163.com
    • 微信:13667225184
    • GitHub:github.com/yangchong21…
    • 博客彙總:github.com/yangchong21…
    • 幹活集中營:Android端技術博客和開源項目審覈員
    • 目前工做狀況:在職狀態
    • 技術項目和博客:GitHub項目7k以上star,follower1.1k以上,發表博客100多篇。
    • 熱愛技術:開源項目和博客屢次被鴻洋,郭霖,Android技術週刊,幹活集中營等等推薦。
    • 學歷:武漢軟件工程職業學院,大專學歷
    • 工做年限:3年多
    • 工做地點:北京
  • 關於近期投遞簡歷一點感想
    • 從進入Android這個行業以來,前兩次幾乎都是朋友內推,面試機會相對容易,都是一個App一我的作或者兩我的作,用戶相對來講並很少。此次想着離職,主要是想進入一個較大的平臺,大概能夠理解爲Android端有個至少四五人,能夠進行技術交流,渴望本身可以在技術上突破,這就像本身平時獨自跑步,和跟着一羣跑馬拉松的人跑步,那種緊張感確定是不同的。
    • 近段時間,嘗試着向一些較大的公司投遞簡歷,大概在拉鉤上投了15個左右(不喜歡海投),發現絕大多數簡歷到不了技術那裏,就被人事說學歷不夠,經驗不夠,工做不匹配等狀況回絕。不過也能夠理解,看簡歷無非就是學歷和經驗,貌似本身的履歷是差了一點。
    • 這大概是第一次在網上發一個主動但願同行內推的介紹,若是你的公司有Android方面的招聘,可否內推一下我這個小人物,感謝。

關於其餘內容介紹

01.關於博客彙總連接

02.關於個人博客

相關文章
相關標籤/搜索