#android #lambda #kotlin
#Android #lambda #kotlin
Вопрос:
Я путаю достоинства написания кода в следующих 2 случаях:
class TestA {
private val foo: Boolean by lazy {
// Here is logic that return true or false
}
Case 1:
fun main() {
TestB({foo})
}
Case 2:
fun main() {
TestB(foo)
}
}
Case 1:
class TestB(private val isFoo: () -> Boolean ) {
fun checkFoo(): Boolean {
return isFoo.invoke()
}
}
Case 2:
class TestB(private val isFoo: Boolean ) {
fun checkFoo(): Boolean {
return isFoo
}
}
Когда я должен использовать вариант 1 или вариант 2?
Кстати, пожалуйста, дайте мне знать, как работает метод invoke ()?
Ответ №1:
Вы передаете лямбды в другие конструкторы классов только в том случае, если хотите, чтобы что-то вызывалось на другом конце, что может иметь смысл, если используется в качестве обратного вызова, или если вам нужна функция, которая создает объекты снова и снова, а не быть статичной. В этом случае вы бы сохранили lambda для последующего использования и вызывали его всякий раз, когда это необходимо. Если вы просто передаете статический экземпляр, который есть, например, foo
в вашем коде, для лямбда-выражения нет причин. Вы всегда должны предпочитать не использовать лямбды для конструкторов; сценарии, в которых они полезны или необходимы, довольно редки, IMO.
Что касается вашего вопроса по invoke
: Котлин имеет ряд функций, которые работают «по соглашению», например, rangeTo
, equals
, contains
, compareTo
, индекс операторами и тоже invoke
. Узнайте о конвенциях здесь.
Теперь, всякий раз, когда класс предоставляет invoke
оператор, вы можете вызывать экземпляры этого класса, как если бы они были функциями:
class InvokeMe(){
operator fun invoke(value: Int) = println("invoked with $value")
}
val obj = InvokeMe()
//both are compiled to the same code
obj(10)
obj.invoke(5)
Поскольку каждый лямбда-код компилируется в Function
экземпляр (см. kotlin.jvm.functions
), который поставляется с реализацией invoke
, вы можете вызывать лямбды, как показано выше, т.е. используя lambda(args)
или lambda.invoke(args)
Ответ №2:
.invoke()
просто вызовет ваш lambda и выдаст ваш результат, такой же, как вызов функции.
Что касается того, когда вы должны передавать лямбда или фактическое значение, это очень зависит.
Лично я бы посоветовал использовать лямбды только в очень специфических ситуациях, чрезмерное их использование может сделать ваш код очень запутанным и трудным для рефакторинга. Если вы просто хотите, чтобы результат был передан в функцию, просто передайте фактическое значение. Не заставляйте кого-то другого вызывать .invoke()
.
Но несколько хороших примеров для лямбда-выражения — это обратные вызовы или onClickListeners.
// A login network request with a lambda handling the result.
fun login( username: String, password: String, onResult: (Result) -> Unit ) {
// do some network call, and return a Result.
}
// note: if the last param is a lambda, you can simply move it outside the function call like this.
login( username, password ) { result ->
// use the result of the network request.
}
Надеюсь, это поможет.
Комментарии:
1. Огромное спасибо @Active-Dog ! Я принял ваш ответ. Еще одна вещь, которую я хочу подтвердить в отношении моего понимания: если я передам lambda как вариант 1, может ли производительность быть лучше? Потому что я думаю, что лямбда-код фактически запускается при вызове checkFoo()? это правильно?
2. @miho39 Я не думаю, что вы заметите какую-либо разницу в производительности, но я не уверен на 100%. Однако вы передаете структуру данных большего размера, так что это может повлиять на ситуацию.