Kotlin о передаче lambda как свойства

#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%. Однако вы передаете структуру данных большего размера, так что это может повлиять на ситуацию.