標籤:

Kotlin 基礎:從 null safety 到 Standard.kt

這篇文章依舊沒有什麼主題、沒有什麼深度,純碎隨筆,談的也是 Kotlin 最最基礎的問題。

我常常說 Kotlin 比 Swift 舒服的一點是,Kotlin 在 null-safety 問題上,會有所謂「smart cast」。例如你有

var a: Foo? = getFoo()nif (a != null) {n print(a.foo) // 不再需要 !!n}n

而在 Swift 里,你仍然需要手動再次「!」

var a: Foo? = getFoo()nif (a != null) {n print(a!.foo) // 仍然需要 !n}n

當然所有人都會很快注意到的一點是,Kotlin 對於局部變數可以做這樣的處理,但如果是個類的成員變數(var),就不行:

class Bar {n var a: Foo? = getFoo()n fun bar() {n if (a != null) {n print(a.foo) // 編譯無法通過n }n }n}n

為什麼 Kotlin 此時不予編譯通過呢?因為在當前線程判斷「a != null」之後,其它線程可能又修改了「a」的值。事實上,如果是用「val」聲明的 field,此時就可以 smart cast。

而如果是「var」聲明的呢?如果你加上「!!」,變成「a!!.foo」,就可以編譯通過,但這麼做的話,你就會被抓住遊街。所以,正確的解決方式是,定義一個局部變數,這樣就確保沒有其它線程能動到它。

fun bar() {n val localA = an if (localA != null) {n print(localA.foo) // cooln }n}n

這樣寫的唯一問題,就是丑。定義了一個可能只用到一次的局部變數,而它並不被整個方法所需要,而僅僅只是臨時用到而已。大約 Swift 程序員們也意識到了這一點,所以 Swift 里習慣的 practice 是:(摘自官方教程)

if let roomCount = john.residence?.numberOfRooms {n print("Johns residence has (roomCount) room(s).")n} else {n print("Unable to retrieve the number of rooms.")n}n

也就是,為了寫得更簡明一些,Swift 從語法層面增加了「if let」語句解決問題。

Kotlin 里,當然也可以利用「let」來寫得漂亮些。當然,Kotlin 的 DSL 能力那麼好,何必再從語法層面解決問題?

print(john.residence?.numberOfRooms?.let { "Johns residence has $it room(s)." } ?: "Unable to retrieve the number of rooms.")n

一氣呵成,不需要任何額外的語法。事實上,我實際寫代碼時,也經常會「foo?.let { … }」這麼用。

上面語句的核心就是「let」。和 Swift 不同,Kotlin 里的「let」不過只是一個普通的函數而已。事實上,很多 Kotlin 教程都不會提及標準庫里這幾個很贊的函數。它們實現得非常簡單,但卻可以作為良好 Kotlin 代碼的典範:

public inline fun <T, R> T.run(block: T.() -> R): R = block()npublic inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()npublic inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }npublic inline fun <T> T.also(block: (T) -> Unit): T { block(this); return this }npublic inline fun <T, R> T.let(block: (T) -> R): R = block(this)npublic inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? = if (predicate(this)) this else nulln

熟悉它們,能夠幫你大大地縮短代碼。

例如某個方法,在計算出返回值後,還需要做一些清理,之前你可能:

fun foo(): Int {n val resource = getResource()n val retVal = resource.calculate()n resource.cleanUp()n return retValn}n

而現在,僅靠標準庫里的這幾個方法,你就可以:

fun foo() = getResource().run { calculate().also { cleanUp() } }n

標準庫里的這幾個方法僅僅是最最常用和最簡單的對控制語句的補充。其實還有很多這樣的擴展方法可以用,例如 funKTionale 裡面的函數,都很棒呢。


推薦閱讀:

#Kotlin# 一年の使用報告 - 類型設計
Kotlin 終於成為了 Android 的官方支持語言
#Kotlin# Activity 之朝花夕拾

TAG:Kotlin |