一個 Boolean 的有趣的擴展
歡迎關注微信公眾號: Kotlin
我定義了一個叫做 isEnabled 的變數:
var isEnabled: Boolean = ... n
後面要用它的時候通常來說我會直接寫出這個變數名,然後想到還得寫個 if ... else,就像這樣:
if(isEnabled) ... else ... n
當然 IntelliJ 也為我們提供了後綴表達式快速輸入 if 語句:
當我想要做一個判斷的時候,我首先想到的是這個變數 isEnabled,於是通常我會很快的把它給打出來,接著我想著如果它為 true,那麼如何如何,這時候還需要把游標移到最開始,寫下 if,儘管我們剛說 IntelliJ 提供了後綴表達式來簡化這個輸入過程,但還是不能改變這個反人類的輸入本質啊。
我想要的就是一個數據處理流,每一次函數調用都是一次數據的變換,if 的出現破壞了流的結構。
相比之下我更喜歡下面的寫法:
isEnabled.yes { n ... n }.otherwise { n ... n } n
如果你還是看不出來 if 到底有什麼「罪過」,那我們再來看一個場景:
args n .map(String::toInt) n .filter{ it % 2 == 0 } n .count().equals(5) n ... n
例子是我瞎寫的,不用在意它的含義。假設我們通過一系列的函數調用得到了一個 Boolean 值,並且需要根據它的值來做出一些操作,那麼我們是不是應該在外面套一層 if else 語句呢?
if(args n .map(String::toInt) n .filter{ it % 2 == 0 } n .count() == 5 n ){ n println("輸入參數中偶數的個數為5") n } else { n println("輸入參數中偶數的個人不為5") n } n
而我們通過擴展完全可以實現下面的寫法:
args n .map(String::toInt) n .filter{ it % 2 == 0 } n .count().equals(5) n .yes { n println("輸入參數中偶數的個數為5") n }.otherwise { n println("輸入參數中偶數的個人不為5") n } n
如果分支語句中還有返回值,我們也可以接著向下執行(當然,對於有返回值的分支語句,我們要求分支完備,也就是必須有 otherwise 調用):
args.map(String::toInt).filter{ n it % 2 == 0 n }.count().equals(5).yes { n "666" n }.otherwise { n "23333" n }.let(::println) n
另外,我們可以稍稍做下修改,讓語法變得更簡單(並且不易懂= =?):
isEnabled { n "666" n } otherwise { n "2333" n } n
不過這樣寫有個弊端,就是在前面的 Boolean 值是個表達式的時候,需要加括弧, otherwise 調用之後如果還有後續操作,那麼也會產生歧義。當然這一步其實不是很必須了,做到前面的步驟已經足夠。
下面我們來看下擴展的源碼:
sealed class BooleanExt<out T> constructor(val boolean: Boolean) n n object Otherwise : BooleanExt<Nothing>(true) n class WithData<out T>(val data: T): BooleanExt<T>(false) n n inline fun <T> Boolean.yes(block: () -> T): BooleanExt<T> = when { n this -> { n WithData(block()) n } n else -> Otherwise n } n n inline fun <T> Boolean.no(block: () -> T) = when { n this -> Otherwise n else -> { n WithData(block()) n } n } n n inline infix fun <T> BooleanExt<T>.otherwise(block: () -> T): T { n return when (this) { n is Otherwise -> block() n is WithData<T> -> this.data n else ->{ n throw IllegalAccessException() n } n } n } n n inline operator fun <T> Boolean.invoke(block: () -> T) = yes(block) n
推薦閱讀:
※Kotlin泛型語法中的歧義性
※廢文(一)
※開源文檔翻譯的質量保障實踐
※Kotlin雜談(一) - 高等函數(Curry + 閉包)
TAG:Kotlin |