標籤:

在 Kotlin 代碼中慎用 lateinit 屬性

有的小夥伴剛開始寫 Kotlin 代碼的時候,會把寫 Java 代碼的習慣也帶過來,比如這樣:

class Demo {n var value: Stringn n fun printValue() {n println(value)n }n}n

當然,這樣寫的後果就是一個編譯錯誤:

Error:(2, 5) Kotlin: Property must be initialized or be abstract

這時候,有的小夥伴看到了 lateinit 修飾符

lateinit var value: Stringn

一編譯,哇,沒有錯誤呢~ 測試一下吧

fun main(args: Array<String>) {n val d = Demo()n d.printValue()n}n

運行!

Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property value has not been initialized

怎麼回事?仔細看看錯誤信息:異常是 UninitializedPropertyAccessException,好像是「訪問未初始化的屬性異常」,後面的錯誤信息說「使用 lateinit 修飾的屬性 value 未初始化」……

What the f**k,難道 value 的值不會默認為 null 嗎?垃圾 Kotlin!!!


lateinit 是一個坑,對於萌新來說還是一個不小的坑。

當我們用 lateinit 修飾類屬性的時候,實際上在告訴編譯器:這個屬性的初始化的時機和方式由我親自把控,你個垃圾編譯器哪兒涼快哪兒待著去。

但可愛的 Kotlin 編譯器不能坐視你把 null 賦給非空的 String 類型呀,很貼心地把這個類里所有訪問 lateinit 屬性的地方都做了一次空檢查:

// 位元組碼等價代碼nfun printValue() {n val tempValue = this.valuen if(tempValue == null) {n throw UninitializedPropertyAccessException("lateinit property value has not been initialized")n }n println(tempValue)n}n

沒錯,你定義的 value 屬性確實是 null,但因為你給 Kotlin 的類型是 String,不能容納 null,Kotlin 為了保證「絕對的空安全」,只能拋出異常了事。這個鍋是誰的?編譯器當然一臉無辜:肯定是你的,誰讓你用 lateinit 呢?年輕人吶,自由是有代價的,你用 lateinit 獲得了片刻的自由,得到的卻是無盡的異常,這個道理,你明白了嗎?

回到正題,lateinit 的蛋疼之處在於,萌新以為找到了接近 Java 的寫法,但實際上一隻腳已經踩在了地雷上;有一定 Kotlin 經驗的同學呢,要麼覺得 lateinit 給的自由度完全不夠,必要的時候直接 「var + 可空類型」浪起,要麼處處擔驚受怕,生怕碰到沒有初始化的屬性。這樣,lateinit 就變成一個十足的雞肋了,會用的不想用,不會用的處處掉坑。

對於 Kotlin 新手來說,應該拋開 Java 式的寫法,牢記類屬性的三種初始化方式:

  • 主構造函數內定義屬性,使用傳入的參數初始化屬性;
  • 類體內定義屬性,同時初始化;
  • 類體內定義屬性,init 塊里初始化。

其他的初始化方式,慎用。

推薦閱讀:

一個 Boolean 的有趣的擴展
Kotlin泛型語法中的歧義性

TAG:Kotlin | 编程 |