kotlin語法之將函數作爲參數

一個setOnClickListener的錯誤

寫了個demo,點擊事件離奇的不生效,非常困擾。

開始實在是看不出有什麼問題,錯誤代碼如下:

findViewById<Button>(R.id.btn).setOnClickListener {
    View.OnClickListener {
        ...
    }
}

運行不報錯,就是不觸發OnClickListener中的代碼。

那麼正確的寫法應該是怎樣呢?

如下所示:

// 寫法1:OnClickListener寫法
findViewById<Button>(R.id.btn).setOnClickListener(
        View.OnClickListener {
            ...
        }
)

或者這樣:

// 寫法2:閉包寫法
findViewById<Button>(R.id.btn).setOnClickListener {
    ...
}

爲什麼那個錯誤的寫法不報錯但是卻不能正確運行呢?

因爲錯誤的寫法實際上是在寫法2的閉包裏面又聲明瞭一個OnClickListener,僅僅聲明listener當然不能調用裏面的代碼了。

將函數作爲參數傳遞

爲什麼setOnClickListener有兩種寫法呢?

實際上這裏setOnClickListener這個方法有兩個重載:

// 參數是OnClickListener對象
fun setOnClickListener(l: View.OnClickListener?)
// 參數是閉包
fun setOnClickListener(l: (v: View) -> Unit)

因爲有兩個重載方法,setOnClickListener纔可以有兩種寫法。

所謂閉包:

在計算機科學中,閉包(英語:Closure),又稱詞法閉包(Lexical Closure)或函數閉包(function closures),是引用了自由變量的函數。這個被引用的自由變量將和這個函數一同存在,即使已經離開了創造它的環境也不例外。

kotlin中的閉包一般稱爲lambda表達式,詳細內容可以查看:

高階函數與 lambda 表達式

我們這裏只看將函數作爲參數傳遞的用法。

因爲閉包這一特性的存在,上面setOnClickListener纔可以不使用傳統的OnClickListener, 下面我們自己定義一個簡單的例子來說明。

傳統寫法:

// 聲明listener
interface TestListener {
    fun test()
}

// 聲明使用listener作爲參數的方法
private fun setListener(listener: TestListener) {
    // 調用listener的test方法
   listener.test()
}

// 方法中使用
fun main(){
    setListener(object : TestListener {
        override fun test() {
            Log.i("test_tag", "test Listener")
        }
    })
}

閉包寫法:

// 聲明方法
private fun setListener(listener: () -> Unit) {
    // 調用傳入的方法
    listener()
}

// 方法中使用
fun main(){
    setListener { Log.i("test_tag", "test unit") }
}

明顯簡化了許多,再也不需要通過聲明接口設置監聽了。

需要額外說明將函數作爲參數傳遞使用的幾種變種:

帶參數的閉包:

// 帶參數
private fun setListener(l: (c: Context) -> Unit) {
    l(applicationContext)
}
// 使用,只有一個參數時,可以用it代替傳入的參數
setListener { Toast.makeText(it, "test param", Toast.LENGTH_SHORT).show() }

帶參數且有返回值:

private fun setListener(l: (c: Context) -> Boolean) {
    val b = l(applicationContext)
    Toast.makeText(applicationContext, if (b) "當前時間是偶數" else "當前時間是奇數", Toast.LENGTH_SHORT).show()
}

// 使用,只有一個參數時,可以用it代替傳入的參數
setListener {
    Toast.makeText(it, "test param", Toast.LENGTH_SHORT).show()
    // 當前時間是否是偶數
    System.currentTimeMillis() % 2 == 0L
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章