简体   繁体   English

Kotlin高阶函数如何工作?

[英]How Kotlin Higher-Order Functions work?

I'm struggling a bit to understand Higher-Order Functions and how to pass functions as parameters to other functions using Kotlin. 我有点想了解高阶函数,以及如何使用Kotlin将函数作为参数传递给其他函数。 I have a basic example that I want to fufill: 我有一个想要补充的基本示例:

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)
    }

I'm calling the method above this way: 我以这种方式调用上面的方法:

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

And updateSearchResults(null) is declared as: 并且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()
        }
    }

I know I passed null to the func when I declared it ('cause I need to pass something at compilation time), however, the call made from inside addOnSearchGameResultListener() is not made passing the parameter from runtime, I mean, in addOnSearchGameResultListener() I always get null for results. 我知道我在声明函数时将null传递给了func(因为我需要在编译时传递一些东西),但是,并不是从addOnSearchGameResultListener()内部进行的调用是从运行时传递参数的,我的意思是在addOnSearchGameResultListener()我总是得到空的结果。 How exactly this works and what am I doing wrong? 这到底是怎么工作的,我在做什么错?

Frankly speaking, I am not entirely sure what your code was to achieve, but let me clarify what your snippet is doing at least: 坦白地说,我不确定您要实现的代码是什么,但是让我澄清一下您的代码段至少在做什么:

private fun updateSearchResults(results : List<Game>?):
         (foo: List<Game>?) -> Unit = { parameter: List<Game>? ->

    if (null != results && results.size > 0) {
        // code
        Unit
    } else {
        // code
        Unit
    }
}

Here you have a function updateSearchResults which accepts a parameter results and returns a function of type (foo: List<Game>?) -> Unit . 在这里,您有一个updateSearchResults函数,该函数接受参数results并返回类型为(foo: List<Game>?) -> Unit的函数。 Note that I renamed some things to avoid name clashes and clarify what is what. 请注意,我重命名了一些名称以避免名称冲突并弄清楚是什么。 The naming foo hast no effect what so ever, I am not sure why you are allowed to write it. 命名foo至今没有任何作用,我不确定为什么允许您编写它。 The returning lambda has one parameter parameter of type List<Game>? 返回的lambda具有一个参数类型为List<Game>? parameter List<Game>? , which you completely ignore in your code. ,您将在代码中完全忽略它。 Over all, the result of the if depends exclusively on the parameter of updateSearchResults . 总体而言, if的结果仅取决于updateSearchResults的参数。

I think the confusion comes from parameter names, results in particular. 我认为混淆来自参数名称,尤其是results To resolve that you can change the updateSearchResults to ie: 要解决此问题,可以将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()
    }
}

However I do feel that it would easier to follow the code if you'd apply following changes: 但是,我确实认为,如果您应用以下更改,则遵循该代码会更容易:

  • make updateSearchResults regular method: 使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() } } 
  • change the addOnSearchGameResultListener invocation and pass a lambda 更改addOnSearchGameResultListener调用并传递一个lambda

     mGameService.addOnSearchGameResultListener( this, releaseThread(), showNoResultsFoundMessage(), { updateSearchResults(it) } ) 
  • apply similar changes to releaseThread , showNoResultsFoundMessage 将类似的更改应用于releaseThreadshowNoResultsFoundMessage

There is an excellent article created by Juan Ignacio Saravia that talk about Higher-Order Functions Juan Ignacio Saravia 撰写精彩文章谈到了高阶函数

I'll try to summarize here: 我将在这里尝试总结:

A higher-order function is a function that takes functions as parameters, or returns a function. 高阶函数是将函数作为参数或返回函数的函数。

Pass a function as parameter 传递函数作为参数

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

This function “logExecution” allows you to pass a function as parameter and log before and after the execution of this function. 此函数“ logExecution”使您可以将函数作为参数传递,并在执行此函数之前和之后进行日志记录。

func: ()-> Unit func:()->单位

Here “func” is the name of the parameter and “() -> Unit” is the “type” of the parameter, in this case, we are saying that func will be a function that doesn't receive any parameter and doesn't return any value (remember that Unit works like void in Java). 在这里, “ func”是参数的名称, “(()-> Unit”)是参数的“类型”,在这种情况下,我们说的是func将是不接收任何参数并且不会接收任何参数的函数。 t返回任何值(请记住,Unit在Java中的作用类似于void)。

You can call this function by passing a lambda expression that must not receive or return any value, like in this way: 您可以通过传递不能接收或返回任何值的lambda表达式来调用此函数,如下所示:

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

but also Kotlin allows you to remove the parenthesis if there is only one function parameter or if the last parameter is a function: 而且Kotlin还允许您在只有一个函数参数或最后一个参数是函数的情况下删除括号:

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

Receive another parameter 接收另一个参数

We can change the logExecution signature to receive another parameter and then put the function parameter at the end like this: 我们可以更改logExecution签名以接收另一个参数,然后将函数参数放在末尾,如下所示:

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

or: 要么:

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

Make the function receive and return values 使函数接收和返回值

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

Async function example 异步功能示例

This is a function that receives a function and execute it in another thread: 这是一个接收一个函数并在另一个线程中执行的函数:

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

and we can execute a function outside of the Main UI Thread easily: 我们可以轻松地在Main UI Thread外部执行一个函数:

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

Maybe you want to run some specific code for Lollipop devices and instead of doing the regular if check, you can use this function: 也许您想为Lollipop设备运行一些特定的代码,而不是执行常规的if检查,可以使用以下功能:

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

and use it in this way: 并以这种方式使用它:

isLollipopOrAbove {
    // run lollipop specific code safely
}

I hope with this, it has become a little clearer about Higher-Order Functions 我希望借此,对高阶函数的了解变得更清晰了

I passed null to the func when I declared it ('cause I need to pass something at compilation time), however, the call made from inside addOnSearchGameResultListener() is not made passing the parameter from runtime 我在声明函数时将null传递给func(因为我需要在编译时传递某些东西),但是,并未通过addOnSearchGameResultListener()内部进行的调用从运行时传递参数

There is no passing at runtime or compile time. 在运行时或编译时没有传递。 If you use the function only once like updateSearchResults(null) , the if is always false, and the whole thing is equivalent to { showNoResultsFoundMessage() } 如果仅使用一次函数updateSearchResults(null)updateSearchResults(null) ,则if始终为false,整个过程等效于{ showNoResultsFoundMessage() }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM