Статьи Kotlin. Лямбда-выражения и анонимные функции
Post
Cancel

Kotlin. Лямбда-выражения и анонимные функции

Лямбда-выражения и анонимные функции - это функции без имени, которые могут быть переданы в качестве аргумента другим функциям. Их можно объявлять отдельно: сохранить в переменной и вызывать в нужном месте. Но обычно в этом нет необходимости, поэтому чаще всего они объявляются непосредственно при передаче в функцию.

Как правило они используются совместно с функциями из стандартной библиотеки Kotlin: с их помощью можно настроить поведение стандартных функций и добавить для них правила.


Синтаксис

Лямбда-выражения

Лямбда-выражения всегда окружены фигурными скобками. Список аргументов отделяется от тела лямбды стрелкой ->.

1
2
val sum = { x: Int, y: Int -> x + y }
println(sum(1, 5))                     // 6

Параметров может быть несколько, как в примере выше, либо может вовсе не быть, тогда левая часть вместе со стрелкой -> опускаются.

1
2
val number = { 1 }
println(number())  // 1

Если в лямбда-выражении только один параметр, при этом его тип может быть выведен автоматически, то объявление параметра можно опустить вместе с ->. В данном случае для обращения к параметру будет создано имя по умолчанию - it.

1
2
3
var double = { x: Int -> x * 2 }
double = { it * 2 + 1 }
println(double(4))                  // 9

Значение из лямбда-выражения можно вернуть явно - при помощи оператора return. Либо неявно будет возвращено значение последнего (а возможно и единственного) выражения.

Если лямбда-выражение является последним аргументом функции, то оно может быть вынесено за круглые скобки. Если же оно является единственным аргументом функции, то круглые скобки можно вовсе опустить. Такой синтаксис также известен как trailing lambda.

1
2
3
4
5
6
7
8
9
10
11
// Все три формы означают одно и тоже

// лямбда передается в качестве аргумента
people.maxBy({ р: Person -> p.age })

// лямбда вынесена за скобки,
// т.к. является последним аргументом функции
people.maxBy() { р: Person -> p.age }

// скобки опущены, т.к. лямбда - единственный аргумент функции
people.maxBy { р: Person -> p.age }

Может встретиться такое, что один из параметров лямбда-выражения не используется. Имя такого параметра можно заменить на символ подчёркивания _. Kotlin умеет отслеживать неиспользуемые параметры, поэтому будет в коде их все выделять и советовать переименовать.

1
map.forEach { _, value -> println("$value!") }

Если лямбда-выражение определено в функции, то оно может обращаться к её параметрам и локальным переменным, объявленным перед лямбда-выражением.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun countQuantity(plants: List<Plant>) {
    var treeCounter = 0
    var flowerCounter = 0
    var another = 0

    plants.forEach {
        when(it.type) {
            "tree" -> treeCounter++
            "flower" -> flowerCounter++
            else -> another++
        }
    }

    println("Trees = $treeCounter, flowers = $flowerCounter, another = $another")
}

Анонимные функции

Анонимная функция объявляется также как и обычная функция, но без указания имени.

1
2
3
4
5
6
7
// анонимная функция в виде выражения
fun(x: Int, y: Int): Int = x + y

// тоже самое, но в виде блока
fun(x: Int, y: Int): Int {
    return x + y
}

В отличии от обычных функций, в анонимной функции можно опустить тип параметров, если его можно вывести из контекста.

1
2
3
4
5
6
fun lookForPine(trees: List<Tree>) {
    trees.filter(fun (tree): Boolean {   // для параметра tree не указывается тип
        println(tree.name)
        return tree.name == "Сосна"
  	})
}

Анонимные функции следуют тем же правилам, что и обычные функции: для функций с телом-выражением возвращаемый тип и оператор return могут быть опущены, но для функций с телом-блоком они должны быть указаны явно (как в примере выше).

1
2
// функция с телом выражением
trees.filter(fun (tree) = tree.name == "Сосна")

Разница между лямбда-выражением и анонимной функцией

Мне, как новичку, было сложно понять разницу между этими понятиями. В результате я для себя выделила следующее:

  • Изначально появилось понятие анонимной функции как функции, у которой нет имени. Такие функции создаются в месте использования, либо присваиваются переменной и потом в нужном месте косвенно вызываются. Хотя в последнем случае функция всё таки получает имя за счет переменной и перестаёт быть анонимной, но тем не менее такой пример использования анонимной функции встречается повсеместно.

  • Лямбда-выражение в общепринятом смысле - это синтаксическая конструкция для объявления анонимной функции. То есть по сути оно позволяет объявлять анонимную функцию в более коротком и читабельном виде.

  • В Kotlin же этим понятиям дали несколько иной смысл, по сути поменяли местами. Под лямбда-выражением понимается небольшой фрагмент кода, который можно передать другой функции. При этом лямбды чаще всего встречаются и применяются. Анонимная же функция - это другая синтаксическая форма лямбда-выражения, которую добавили для более удобной реализации следующих случаев (они же являются и различиями между лямбдой и анонимной функцией):

    • Необходимость в определении возвращаемого типа. Лямбда-выражение выводит тип возвращаемого значения самостоятельно, а в анонимной функции его можно задать явно. Данный случай использования анонимной функции описан в официальной документации языка.

    • Второй случай связан с оператором return. В лямбда-выражении оператор return производит выход из функции, в которой вызывается это лямбда-выражение (т.е. полностью завершает работу этой функции и код, указанный после оператора return никогда не выполнится). Чтобы завершить работу только лямбда-выражения, к оператору return должна быть добавлена метка, но такой синтаксис считается неуклюжим, потому что может запутать при наличии нескольких выражений return.

      В анонимной же функции оператор return завершает работу самой анонимной функции. К тому же её намного удобнее использовать, когда требуется написать блок кода с несколькими точками выхода. Данное отличие описано в официальной документации, но более понятно объяснено на stackoverflow.


Полезные ссылки

Higher-Order Functions and Lambdas - официальная документация.
Высокоуровневые функции и лямбды - перевод на русский язык.
Kotlin Programmer Dictionary: Function Type vs Function literal vs Lambda expression vs Anonymous function - небольшая статья-словарь о функциональных типах, лямбда-выражениях и анонимных функциях.

This post is licensed under CC BY 4.0 by the author.