Лямбда-выражения представляют небольшие кусочки кода, которые выполняют некоторые действия. Фактически лямбды преставляют сокращенную запись функций. При этом лямбды, как и обычные и анонимные функции, могут передаваться в качестве значений переменным и параметрам функции.
Лямбда-выражения оборачиваются в фигурные скобки:
{println("hello")}
В данном случае лямбда-выражение выводит на консоль строку "hello".
Лямбда-выражение можно сохранить в обычную переменную и затем вызывать через имя этой переменной как обычную функцию.
fun main() { val hello = {println("Hello Kotlin")} hello() hello() }
В данном случае лямбда сохранена в переменную hello и через эту переменную вызывается два раза. Поскольку лямбда-выражение представляет сокращенную форму функции,
то переменная hello
имеет тип функции () -> Unit
.
val hello: ()->Unit = {println("Hello Kotlin")}
Также лямбда-выражение можно запускать как обычную функцию, используя круглые скобки:
fun main() { {println("Hello Kotlin")}() }
Следует учитывать, что если до подобной записи идут какие-либо инструкции, то Kotlin автоматически может не определять, что определения лямбда-выражения составляет новую инструкцию. В этом случае предыдущую инструкции можно завершить точкой с запятой:
fun main() { {println("Hello Kotlin")}(); {println("Kotlin on Metanit.com")}() }
Лямбды как и функции могут принимать параметры. Для передачи параметров используется стрелка ->. Параметры указываются слева от стрелки, а тело лямбда-выражения, то есть сами выполняемые действия, справа от стрелки.
fun main() { val printer = {message: String -> println(message)} printer("Hello") printer("Good Bye") }
Здесь лямбда-выражение принимает один параметр типа String, значение которого выводится на консоль. Переменная printer
в данном случае имеет тип (String) -> Unit
.
При вызове лямбда-выражения сразу при его определении в скобках передаются значения для его параметров:
fun main() { {message: String -> println(message)}("Welcome to Kotlin") }
Если параметров несколько, то они передаются слева от стрелки через запятую:
fun main() { val sum = {x:Int, y:Int -> println(x + y)} sum(2, 3) // 5 sum(4, 5) // 9 }
Если в лямбда-выражении надо выполнить не одно, а несколько действий, то эти действия можно размещать на отдельных строках после стрелки:
val sum = {x:Int, y:Int -> val result = x + y println("$x + $y = $result") }
Выражение, стоящее после стрелки, определяет результат лямбда-выражения. И этот результат мы можем присвоить, например, переменной.
Если лямбда-выражение формально не возвращает никакого результата, то фактически, как и в функциях, возвращается значение типа Unit:
val hello = { println("Hello")} val h = hello() // h представляет тип Unit val printer = {message: String -> println(message)} val p = printer("Welcome") // p представляет тип Unit
В обоих случаях используется функция println, которая формально не возвращает никакого значения (точнее возвращает объект типа Unit).
Но также может возвращаться конкретное значение:
fun main() { val sum = {x:Int, y:Int -> x + y} val a = sum(2, 3) // 5 val b = sum(4, 5) // 9 println("a=$a b=$b") }
Здесь выражение справа от стрелки x + y
продуцирует новое значение - сумму чисел, и при вызове лямбда-выражения это значение
можно передать переменной. В данном случае лямбда-выражение имеет тип (Int, Int) -> Int
.
Если лямбда-выражение многострочное, состоит из нескольких инструкций, то возвращается то значение, которое генерируется последней инструкцией:
val sum = {x:Int, y:Int -> val result = x + y println("$x + $y = $result") result }
Последнее выражение по сути представляет число - сумму чисел x и y и оно будет возвращаться в качестве результата лямбда-выражения.
Лямбда-выражения можно передавать параметрам функции, если они представляют один и тот же тип функции:
fun main() { val sum = {x:Int, y:Int -> x + y } doOperation(3, 4, sum) // 7 doOperation(3, 4, {a:Int, b: Int -> a * b}) // 12 } fun doOperation(x: Int, y: Int, op: (Int, Int) ->Int){ val result = op(x, y) println(result) }
При передаче лямбды параметру или переменной, для которой явным образом указан тип, мы можем опустить в лямбда-выражении типы параметров:
fun main() { val sum: (Int, Int) -> Int = {x, y -> x + y } doOperation(3, 4, {a, b -> a * b}) } fun doOperation(x: Int, y: Int, op: (Int, Int) ->Int){ val result = op(x, y) println(result) }
Здесь в случае с переменной sum
Kotlin видит, что ее тип (Int, Int) -> Int
, то есть и первый, и второй параметр представляют тип
Int
. Поэтому при присвоении переменной лямбды {x, y -> x + y }
Kotlin автоматически поймет, что параметры x и y представляют именно тип Int
.
То же самое касается и вызова функции doOperation()
- при передаче в него лямбды Kotlin автоматически поймет какой параметр какой тип представляет.
Если параметр, который принимает функцию, является последним в списке, то при передачи ему лямбда-выражения, саму лямбду можно прописать после списка параметров.
Например, возьмем выше использованную функцию doOperation()
:
fun doOperation(x: Int, y: Int, op: (Int, Int) ->Int){ val result = op(x, y) println(result) }
Здесь параметр, который представляет функцию - параметр op
, является последним в списке параметров. Поэтому вместо того, чтобы написать так:
doOperation(3, 4, {a, b -> a * b}) // 12
Мы также можем написать так:
doOperation(3, 4) {a, b -> a * b} // 12
То есть вынести лямбду за список параметров. Это так называемая конечная лямбда или trailing lambda
Также фукция может возвращать лямбда-выражение, которое соответствует типу ее возвращаемого результата:
fun main() { val action1 = selectAction(1) val result1 = action1(4, 5) println(result1) // 9 val action2 = selectAction(3) val result2 = action2(4, 5) println(result2) // 20 val action3 = selectAction(9) val result3 = action3(4, 5) println(result3) // 0 } fun selectAction(key: Int): (Int, Int) -> Int{ // определение возвращаемого результата when(key){ 1 -> return {x, y -> x + y } 2 -> return {x, y -> x - y } 3 -> return {x, y -> x * y } else -> return {x, y -> 0 } } }
Обратим внимание на предыдущий пример на последнюю лямбду:
else -> return {x, y -> 0 }
Если в функцию selectAction()
передается число, отличное от 1, 2, 3, то возвращается лямбда-выражение, которое просто возвращает число 0.
С одной стороны, это лямбда-выражение должно соответствовать типу возвращаемого результата функции selectAction()
- (Int, Int) -> Int
С другой стороны, оно не использует параметры, эти параметры не нужны. В этом случае вместо неиспользуемых параметров можно указать прочерки:
else -> return {_, _ -> 0 }