Lambda — е анонимна функция, представена под формата на компактен израз { аргументи -> тяло }. В Kotlin ламбдите са основният начин за предаване на поведение на функции от по-висок ред (higher-order functions), осигурявайки лаконичен синтаксис без необходимост от деклариране на отделна функция. Според документацията на Kotlin (2026), над 70% от функциите от по-висок ред в типичните проекти се извикват с ламбди. Lambda позволява писане на код във функционален стил с минимален синтактичен шум.
Основни точки
Lambda (ламбда израз) — е анонимна функция, дефинирана като израз, а не декларация. Терминът произхожда от ламбда смятането на Алонзо Чърч (1936). В програмирането ламбдата представлява блок код, който може да бъде предаден като стойност, съхранен в променлива и извикан по-късно. В Kotlin ламбдата винаги е затворена във фигурни скоби { }, аргументите са отделени от тялото със стрелка ->.
Ламбдата се различава от обикновената функция по липсата на име и ключова дума fun. Това позволява деклариране на поведение директно на мястото на използване, без да се затрупва кода с допълнителни декларации. Компилаторът на Kotlin извежда типовете на аргументите от контекста, така че в много случаи типовете могат да бъдат пропуснати. Ламбдата е синтактична захар над функционалния тип (T) -> R: всяка ламбда има съответен функционален тип.
Основното приложение на ламбдите в Kotlin е предаването им на функции от по-висок ред (higher-order functions). Стандартната библиотека използва ламбди навсякъде: filter { it > 0 }, map { it.uppercase() }, forEach { println(it) }. Ламбдите се използват също за създаване на слушатели на събития, обратни извиквания, конфигуратори на обекти чрез apply и run, което прави кода по-декларативен в сравнение с анонимните класове.
Пълната форма на ламбда израза в Kotlin: { аргументи -> тяло_на_ламбда }. Аргументите се изброяват със запетая, следвани от стрелка и тяло — един или повече израза. Ако тялото съдържа няколко реда, последният израз става връщаната стойност. Ако функцията приема ламбда като последен аргумент, тя може да бъде изнесена извън скобите на извикване.
val sum: (Int, Int) -> Int = { a, b -> a + b }
// Типове, изведени от контекста
val sumShort = { a: Int, b: Int -> a + b }
// Извикване на ламбда
val result = sum(5, 3) // 8
println(result)
Променливата sum е декларирана с изричен функционален тип (Int, Int) -> Int и инициализирана с ламбда { a, b -> a + b }. За sumShort типовете са посочени вътре в ламбдата, а типът на променливата е изведен. И двата варианта са еквивалентни. Извикването на ламбда не се различава от извикването на обикновена функция — използва се синтаксис sum(5, 3). Ламбда без аргументи се записва като { body }, с един аргумент — { it -> it * 2 } или просто { it * 2 }.
Когато ламбдата приема точно един аргумент, Kotlin предоставя неявното име it. Това елиминира необходимостта от деклариране на аргумента от лявата страна. Програмистът пише само тялото на ламбдата, използвайки it за достъп до единствения параметър. Такъв синтаксис е особено удобен във вериги за обработка на колекции, където it обозначава текущия елемент.
val numbers = listOf(1, 2, 3, 4, 5)
// Изрично име на параметър
val squaredExplicit = numbers.map { n -> n * n }
// Неявно it
val squared = numbers.map { it * it }
// Филтриране с it
val even = numbers.filter { it % 2 == 0 }
println(squared) // [1, 4, 9, 16, 25]
В примера map { it * it } се използва it вместо декларация n ->. Kotlin автоматично присвоява it за единствения параметър на ламбдата. Ако ламбдата приема два или повече аргумента, it не се създава — всички параметри трябва да бъдат декларирани изрично. За по-добра четимост на вложени ламбди се препоръчва да се дават на параметрите смислени имена, а it да се използва в кратки едноредови предикати.
Trailing lambda (завършваща ламбда) — синтактична особеност на Kotlin, която позволява последният аргумент-ламбда да бъде поставен извън кръглите скоби на извикване на функцията. Това прави кода подобен на вградени езикови конструкции. Ако ламбдата е единственият аргумент, скобите могат да бъдат напълно пропуснати. Този подход се използва активно в стандартната библиотека и DSL.
// Стандартно извикване
thread(block = {
println("Здравей от нишка")
})
// Версия с trailing lambda
thread {
println("Здравей от нишка")
}
// repeat с trailing lambda
repeat(3) { index ->
println("Итерация $index")
}
// run с многоредова ламбда
val config = run {
val host = "localhost"
val port = 8080
"$host:$port"
}
Функцията thread приема Runnable като последен аргумент. Благодарение на trailing lambda, ламбдата може да бъде предадена извън скобите, което визуално наподобява блок код. Функцията repeat приема брой итерации и ламбда с индекс. run — пример за функция от по-висок ред, където многоредова ламбда изчислява стойността, връщана от последния израз. Скобите около run() могат да бъдат пропуснати, ако ламбдата е единственият аргумент.
Залавяне на променливи (closure) — способността на ламбдата да достъпва променливи, декларирани във външния обхват на видимост. В Kotlin ламбдата може да чете и променя var променливи, както и да чете val променливи от околния контекст. Заловените променливи се съхраняват заедно с ламбдата през целия ѝ жизнен цикъл, което позволява създаването на функции със състояние.
fun makeCounter(): () -> Int {
var count = 0
return {
count++ // брой залавяния
}
}
fun main() {
val counter = makeCounter()
println(counter()) // 0
println(counter()) // 1
println(counter()) // 2
}
Функцията makeCounter декларира локална променлива count и връща ламбда { count++ }. Въпреки че makeCounter е приключила работата си, върнатата ламбда запазва референция към count чрез затваряне (closure). Всяко извикване на counter() увеличава и връща текущата стойност. Механизмът closure стои в основата на къринг, фабрики за функции и мемоизация. Важно е да се помни, че залавянето на променливи променливи може да доведе до неочаквано поведение при паралелно изпълнение.
Често задавани въпроси
Ламбда не поддържа безусловен return — return без етикет връща управлението на външната функция. За излизане от ламбда използвайте return@label, където label е името на функцията от по-висок ред, на която е предадена ламбдата.
Анонимна функция се записва като fun(a: Int): Int { return a * 2 } и поддържа return без етикет. Ламбда { a -> a * 2 } е синтактично по-кратка, но изисква return@label за ранно излизане.
Ако ламбдата не приема аргументи, стрелката се пропуска: { body() }. Ако приема един аргумент и не го използва, вместо стрелка се пише { _ -> body() } с долна черта за игнориране на параметъра.
Да, всяка ламбда, предадена на non-inline функция, създава инстанция на анонимен клас. За inline функции ламбдата се вгражда на мястото на извикване без създаване на обект. Незалавящите (non-capturing) ламбди се кешират в singleton обекти.
Return в Kotlin е оператор за преход, принадлежащ на най-близката функция с fun. Ламбдата не е функция (fun), а израз. Следователно return вътре в ламбда без етикет се счита за връщане от външната обхващаща функция.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също