Как работают функции высшего порядка 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 ответов
Я думаю, что путаница происходит от названия параметра 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()
}
}
однако я чувствую, что было бы проще следовать коду, если бы вы применили следующие изменения:
-
сделать
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
вызов и передать лямбдаmGameService.addOnSearchGameResultListener( this, releaseThread(), showNoResultsFoundMessage(), { updateSearchResults(it) } )
применить подобные изменения
releaseThread
,showNoResultsFoundMessage
честно говоря, я не совсем уверен, что ваш код должен был достичь, но позвольте уточнить, что код делает, по крайней мере:
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
. Обратите внимание, что я переименовал некоторые вещи, чтобы избежать столкновения имен и уточнить, что есть что. Именование foo
не имеет никакого эффекта, что так когда-либо, я не уверен, почему вам разрешено писать его. Возвращение лямда с одним параметром parameter
типа List<Game>?
, которые вы полностью игнорируете в своем коде. Над всем, результат if
зависит исключительно от параметра updateSearchResults
.
Я передал null в func, когда я его объявил (потому что мне нужно передать что-то во время компиляции), однако вызов, сделанный изнутри addOnSearchGameResultListener (), не передается параметру из среды выполнения
нет передачи во время выполнения или во время компиляции. Если вы используете функцию только один раз, как updateSearchResults(null)
, the if
всегда ложно, и все это эквивалентно { showNoResultsFoundMessage() }
есть отличная статья создано Хуаном Игнасио Саравиа, которые говорят о Функции Более Высокого Порядка
я попытаюсь подвести итог здесь:
функции высшего порядка-это функция, которая принимает функции параметры или возвращает функцию.
передать функцию в качестве параметра
fun logExecution(func: () -> Unit) {
Log.d("tag", "before executing func")
func()
Log.d("tag", "after executing func")
}
эта функция "logExecution" позволяет передать функцию как параметр и журналов до и после выполнения этой функции.
func: () - > Unit
здесь "func" - это имя параметра, а "() - > Unit" является "типом" параметра, в этом случае мы говорим, что func будет функцией, которая не получает никакого параметра и не возвращает никакого значения (помните, что единица работает как void в Java).
вы можете вызвать эту функцию, передав лямда выражение, которое не должно получать или возвращать какое-либо значение, например:
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")
}
пример функцию async
это функция, которая получает функцию и выполняет ее в другом потоке:
fun runAsync(func: () -> Unit) {
Thread(Runnable { func() }).start()
}
и мы можем легко выполнить функцию вне основного потока пользовательского интерфейса:
runAsync {
// i.e.: save something in the Database
}
возможно, вы хотите запустить определенный код для устройств Lollipop и вместо этого для выполнения обычной проверки if вы можете использовать эту функцию:
fun isLollipopOrAbove(func: () -> Unit) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
func()
}
}
и используйте его таким образом:
isLollipopOrAbove {
// run lollipop specific code safely
}
Я надеюсь, что с этим, стало немного яснее о функциях более высокого порядка