本日內容為 Effective Kotlin - 3:Eliminate platform types as soon as
possible
Kotlin 對於空值的安全性是其優勢之一,有助於避免 Java 中經常出現的空指針異常(NPE - NullPointerException)。Java 有滿多好用的函式庫,在開發 Kotlin 會有很大機會用到 JVM 來的值或類型。 當 Kotlin 與其他如 Java 這樣沒有強制的空值安全性的語言交互時,會出現所謂的「平台類型」問題。這種類型的可空性是未知的,且不能在 Kotlin 程式碼中明確表示。
如果我們使用一個聲明 String 作為返回類型的 Java 方法,那麼在 Kotlin 中應該是什麼類型呢?如果它帶有@Nullable 註釋,那麼我們認為它是可為空的,我們將其解釋為 String?。如果它帶有 @NotNull 註釋,那麼我們信任這個註釋,並將其類型為 String。這裡的 Annotation 是一種開發者間的 contract。常見有以下的 nullable 註釋
但是,如果這個返回類型既沒有帶 @Nullable 註釋,也沒有帶 @NotNull 註釋,那又應該怎麼辦呢?這個也是常見的平台類型的狀況。平台類型在類型名稱後用一個驚嘆號!來表示,如 String!,但這種表示法不能在程式碼中使用。平台類型是不可表示的,這意味著不能在 Kotlin 中明確地寫出它們。當一個平台值被分配給 Kotlin 變數或屬性時,它可以被推斷,但不能被明確設定。相反,我們可以選擇我們期望的類型:一種是可為空的,一種是非空的類型
假設有一個 Java 方法如下
public class UserRepo {
public User getUser() {
//...
}
}
那在 Kotlin 取用時 type 的宣告就很重要
val repo = UserRepo()
val user1 = repo.user // Type of user1 is User!
val user2: User = repo.user // Type of user2 is User
val user3: User? = repo.user // Type of user3 is User?
將平台類型轉型為非空類型比完全不指定類型要好,但仍然是危險的因為我們認為是非空的東西可能是空的。
基於安全原因,建議是盡快消除這些平台類型。請參考在此例中 statedType 和 platformType 功能的行為差異。
public class JavaClass {
public String getValue() {
return null;
}
}
在 Kotlin 中取用
fun statedType() {
val value: String = JavaClass().value
//...
println(value.length)
}
fun platformType() {
val value = JavaClass().value
//...
println(value.length)
}
兩種情況下都會導致NPE(空指針異常),但這兩種情況下的錯誤發生的地方是不同的。在 statedType 中,NPE會在從Java 獲取值的那一行中被拋出。可以在 debug 時直接找到發生地,並根據這一變更調整我們的 Code。
而在 platformType 中,當我們使用這個值時(可能是從一個更複雜的表達式中),NPE會被拋出,那就會遠離發生地,更糟的是他可能經過層層散撥。像病毒一樣最後才被發現。
建議作法是會料敵從寬,儘量以 ? 接進來,不管是 JVM 來的。或是前端的 form, 後端的 db, 都假設有為空的可能。經過檢查後再轉成自已 Domain Type, 這樣一來不用一直在 service 處理 Nullable.
因此,當與其他語言交互時,必須格外小心,避免進入Java中類似的困境,即因不當使用對象而導致的NPE。雖然Kotlin 提供了強大的空值安全機制,但在與其他語言的交互中仍需謹慎,以程式碼的穩健性和安全性
這首是 (G)I-DLE 進軍美國的 I DO