Kotlin高阶函数如何工作?

时间:2016-07-07 02:36:52

标签: android kotlin

我正在努力理解高阶函数以及如何使用Kotlin将函数作为参数传递给其他函数。我有一个基本的例子,我想填补:

fun addOnSearchGameResultListener(
            activity: AppCompatActivity,
            releaseThread: () -> Unit,
            showNoResultsFoundMessage: () -> Unit,
            updateSearchResults: (result: List<Game>) -> Unit) {
        var event0017Handler: TaskExecutor = object : TaskExecutor {
            override fun executeOnSuccessTask(response: JSONObject) {
                async() {
                    uiThread {
                        try {
                            releaseThread()
                            mLoaderManager.hideIndeterminateProgressBar(activity)
                            val result = mJSONParser.getGamesByGameKey(response)
                            Log.i(GameController::class.simpleName, "response: ${result.toString()}")
                            updateSearchResults(result)
                        } catch (e: JSONException) {
                            showNoResultsFoundMessage()
                        }
                    }
                }
            }

            override fun executeOnErrorTask(payload: JSONObject) {
                releaseThread()
                mNotificationManager.showErrorPopUp(activity, payload.getString("data"))
            }
        }
        NotificationCenter.RegistrationCenter.registerForEvent(EventCatalog.e0017, event0017Handler)
    }

我以这种方式调用上面的方法:

mGameService.addOnSearchGameResultListener(
            this,
            releaseThread(),
            showNoResultsFoundMessage(),
            updateSearchResults(null)
    )

updateSearchResults(null)被声明为:

private fun updateSearchResults (results : List<Game>?) : (results : List<Game>?) -> Unit = {
        if (null != results && results.size > 0) {
            mLastMatchingQuery = query_container.text.toString()
            hideNoResultsFoundMessage()
            mGames = results
            mAdapter!!.dataSet = results.toMutableList()
        } else {
            showNoResultsFoundMessage()
        }
    }

我知道我在声明它时将null传递给了func(因为我需要在编译时传递一些东西),但是,从addOnSearchGameResultListener()内部调用不会传递参数来自运行时,我的意思是,在addOnSearchGameResultListener()我总是得到null的结果。这究竟是如何运作的,我做错了什么?

4 个答案:

答案 0 :(得分:2)

坦率地说,我不完全确定你的代码是什么,但让我澄清你的代码片段至少在做什么:

A

这里有一个函数private fun updateSearchResults(results : List<Game>?): (foo: List<Game>?) -> Unit = { parameter: List<Game>? -> if (null != results && results.size > 0) { // code Unit } else { // code Unit } } ,它接受​​参数updateSearchResults并返回类型为results的函数。请注意,我重命名了一些东西,以避免名称冲突,并澄清什么是什么。命名(foo: List<Game>?) -> Unit没有任何影响,我不知道为什么你被允许写它。返回的lambda有一个foo类型的parameter参数,您可以在代码中完全忽略它。总而言之,List<Game>?的结果完全取决于if的参数。

答案 1 :(得分:2)

我认为混淆来自参数名称,特别是results。要解决此问题,您可以将updateSearchResults更改为例如:

private fun updateSearchResults() : (List<Game>?) -> Unit = { results ->
    if (null != results && results.size > 0) {
        mLastMatchingQuery = query_container.text.toString()
        hideNoResultsFoundMessage()
        mGames = results
        mAdapter!!.dataSet = results.toMutableList()
    } else {
        showNoResultsFoundMessage()
    }
}

但是,如果您应用以下更改,我觉得更容易遵循代码:

  • make updateSearchResults常规方法:

    private fun updateSearchResults (results : List<Game>?) {
        if (null != results && results.size > 0) {
            mLastMatchingQuery = query_container.text.toString()
            hideNoResultsFoundMessage()
            mGames = results
            mAdapter!!.dataSet = results.toMutableList()
        } else {
            showNoResultsFoundMessage()
        }
    }
    
  • 更改addOnSearchGameResultListener调用并传递lambda

    mGameService.addOnSearchGameResultListener(
            this,
            releaseThread(),
            showNoResultsFoundMessage(),
            { updateSearchResults(it) }
    )
    
  • releaseThreadshowNoResultsFoundMessage

  • 应用类似的更改

答案 2 :(得分:0)

  

当我声明它时,我将null传递给func(因为我需要在编译时传递一些东西),但是,从addOnSearchGameResultListener()内部调用不会从运行时传递参数

在运行时或编译时没有传递。如果您仅使用updateSearchResults(null)一次的函数,则if始终为false,整个事件等同于{ showNoResultsFoundMessage() }

答案 3 :(得分:0)

Juan Ignacio Saravia创建了excellent article,讨论高阶函数

我将尝试总结一下:

  

高阶函数是一个函数,它将函数作为   参数,或返回一个函数。

将函数作为参数传递

fun logExecution(func: () -> Unit) {
    Log.d("tag", "before executing func")
    func()
    Log.d("tag", "after executing func")
}

此函数“logExecution”允许您在执行此函数之前和之后传递函数作为参数和日志。

func :() - &gt;单元

此处“func”是参数的名称,“() - &gt;单位“是参数的”类型“,在这种情况下,我们说func将是一个不接收任何参数并且不返回任何值的函数(请记住,单位的工作方式类似于void爪哇)。

您可以通过传递一个必须不接收或返回任何值的lambda表达式来调用此函数,如下所示:

logExecution( { Log.d("tag", "I'm a function") } )

但是如果只有一个函数参数或者最后一个参数是函数,Kotlin还允许你删除括号:

logExecution { Log.d("tag", "I'm a function") }

接收其他参数

我们可以更改logExecution签名以接收另一个参数,然后将函数参数放在最后,如下所示:

// added tag parameter:
fun logExecution(tag: String, func: () -> Unit) { ... }
// call in this way:
logExecution("tag") { Log.d("tag", "I'm a function") }

或:

logExecution("tag") {
    Log.d("tag", "I'm a function")
}

使函数接收和返回值

fun logExecution(func: (String, String) -> Int) {
    val thisIsAnInt = func("Hello", "World")
}

异步功能示例

这是一个接收函数并在另一个线程中执行它的函数:

fun runAsync(func: () -> Unit) {
    Thread(Runnable { func() }).start()
}

我们可以轻松地在主UI线程之外执行一个函数:

runAsync {
    // i.e.: save something in the Database
}

也许你想为Lollipop设备运行一些特定的代码,而不是做常规的if check,你可以使用这个函数:

fun isLollipopOrAbove(func: () -> Unit) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        func()
    }
}

并以这种方式使用它:

isLollipopOrAbove {
    // run lollipop specific code safely
}

我希望有了它,它对高阶函数有了更清晰的认识