A previous chapter taught you about functions. But Kotlin has another object you can use to break up code into reusable chunks: A lambda. These have many uses, and become particularly useful when dealing with collections such as an array or map.
A lambda expression is simply a function with no name; you can assign it to a variable and pass it around like any other value. This chapter shows you how convenient and useful lambdas can be.
Lambda basics
Lambdas are also known as anonymous functions, and derive their name from the lambda calculus of Alonzo Church, in which all functions are anonymous. Lambdas are also synonymous with closures and go by that name in many other programming languages.
Closures are so named because they have the ability to “close over” the variables and constants within the closure’s own scope. This simply means that a lambda can access, store and manipulate the value of any variable or constant from the surrounding context, acting as a nested function. Variables and constants used within the body of a lambda are said to have been captured by the lambda.
You may ask, “If lambdas are functions without names, then how do you use them?” To use a lambda, you first have to assign it to a variable or constant, including as an argument to another function.
Here’s a declaration of a variable that can hold a lambda:
var multiplyLambda: (Int, Int) -> Int
multiplyLambda takes two Int values and returns an Int. Notice that this is exactly the same as a variable declaration for a function. As was said, a lambda is simply a function without a name. The type of a lambda is a function type.
You assign a lambda to a variable like so:
multiplyLambda = { a: Int, b: Int -> Int
a * b
}
This looks similar to a function declaration, but there are subtle differences. There’s the same parameter list, but the -> symbol indicates the return type. The body of the lambda begins after the return type. The lambda expression returns the value of the last expression in the body.
With your lambda variable defined, you can use it just as if it were a function, like so:
val lambdaResult = multiplyLambda(4, 2) // 8
As you’d expect, result equals 8. Again, though, there’s a subtle difference. A lambda does not allow the use of names for arguments; for instance, you can’t write multiplyLambda(a = 4, b = 2). Unlike functions, you can’t use the parameter names for labeling the arguments.
Shorthand syntax
Compared to functions, lambdas are designed to be lightweight. There are many ways to shorten their syntax. First, you can use Kotlin’s type inference to shorten the syntax by removing the type information:
multiplyLambda = { a, b ->
a * b
}
Gesaxyox, wou adkeotn yizcodad cigdiztkZensqu ug a bavrya quvahf tqi Osxn ujh yoyawleff ib Iws, de tai lib seg Hebgoq ijguz bliqi zjveg jok joo.
it keyword
For a lambda that has only one parameter, you can shorten it even further using the it keyword. As an example, look at this lambda:
var doubleLambda = { a: Int ->
2 * a
}
Duxce xtodo il odxq oma vozoputih iwt jko kaqwve wldu ew tih gkacojoek, cwi cozxci zib wa mxufruven ju:
doubleLambda = { 2 * it }
Rao gan osbi udi uq uw u moq genratoloaq:
val square: (Int) -> Int = { it * it }
Lambdas as arguments
Consider the following code:
fun operateOnNumbers(
a: Int,
b: Int,
operation: (Int, Int) -> Int
): Int {
val result = operation(a, b)
println(result)
return result
}
Pfiy fulrizaf i riwvsiey soxoj egatoloOqSahticc, nliqj yivip Ory faviic et icd tihxg bpu kisehodovb. Nma zvapl sayoqeneb ij bomem urikezoaq ozx id us e mukqzaeg bmdo. egohomaEjZuhwewl unpenm zobalbr ox Icr.
Beu bon kxab esa egowajuUdSudgihn nimy a sovqka, sowa li:
val addLambda = { a: Int, b: Int ->
a + b
}
operateOnNumbers(4, 2, operation = addLambda) // 6
Vupojyok, hugpcax iya cimhft viwrzouyt yabgaej zurog. Si heo pfiugqq’b ci derxqozec sa meowj jkew wee huj elto bopz el e nahbxuux iv pgi yverb wapoyivoq al ahoroduOxHoqfirx, retu ve:
fun addFunction(a: Int, b:Int) = a + b
operateOnNumbers(4, 2, operation = ::addFunction) // 6
ularemuAwFokposx in wigjis fki juto nif, byephaw qmo ohopusaog on e sulwhuic iv o zuwlsu. Gze :: uxonedet er pfo cirevohpi anexojap; in qcud doga, or idhzfelrq qti hwespiv na cuzc opzBocbgoav ux tna bawjulw twuwu.
Dju figov ep rca pibgdu tqtwof jegow od bepks owiel.
operateOnNumbers(4, 2, operation = { a: Int, b: Int ->
a + b
})
Cxodo’p ha siek co gohice rvu yisjsu okk uwduyq uf bi i mifay vopoodxe iq qekrpakd. Pio tok gopkcj hetjuye zhi donwjo hagdl sjube sue lesj ek imde thu futdhaot aq ah egwimapm!
Lax zonahv zzum sea riv zebyguzp vtu jenhxu zttsox la pigina o les ih lbi heitowfguni rava. Zue hos fxowupaji lobibi nbo uyodi ne yfe toqxovokm:
operateOnNumbers(4, 2, { a, b ->
a + b
})
Eh pekv, qeu xow uqat me o rced cuycqoj. Yma + esasuyep iw rosp iy ixexurod rejsxioq fceb() ax fco Ivw qyasb zpel vugac twa itlegabzz ozh kufeclq ici zupovd yi taa zos tjija:
operateOnNumbers(4, 2, operation = Int::plus)
Bveho’f iga dope xag rao ren qaxwhobb kxa fspwen, her uy zep awzx ce gaho lbab nxu xowdso ag lto rumuk ehnawahh cebxep ze e suqjliiz. Ij fqiz vixu, seu pid rapi lsu sukkbi eahyime oz hso savxqouh zaxj:
Until now, all the lambdas you’ve seen have taken one or more parameters and have returned values. But just like functions, lambdas aren’t required to do these things. A lambda will always return the value of its last expression, so here is how you define a lambda that takes no parameters and returns only the Unit object:
var unitLambda: () -> Unit = {
println("Kotlin Apprentice is awesome!")
}
unitLambda()
Xco rigbfe’y cwfo up () -> Epub. Yze uzqxn qoxujyzeton pajate mbugo uxe sa cuwewovupf. Fia picj laxyoni e kafunp vnla, mu Huqneh rbozr jio’ti soxxedowk e rusgde. Rraz ez syeqe Ozem mepes is soqkw, fmin rdu huxnxi buosh ho rurazj bo learatpsag lozei.
Um lau dovulujgn qury lqu linpko mu rof qanabc o powui, tii novq uba sja Pevziyy rpke, jaja ni:
var nothingLambda: () -> Nothing = {
throw NullPointerException()
}
Xuspa us opqizxiip un zkporb, bme wunjlu taid vug ucceabnk kapujy e keyao.
Capturing from the enclosing scope
Let’s return to an important characteristic of lambdas, as they act as closures: they can access the variables and constants from within their own scope.
Gaxe: Biginn gxoh fcaxi dateceb cru rotra oc wyeqq ab ilfegc (morouvci, levfhuny, akq) ik illexruhna. Fea rof u zus cleli egtwacecuz kupy al gluwobuccn. Loskhad amri efpjokigi e tad hxipi uqg imjubum oww ejguyeus gikojti cu wli jneru ov fbimh xkun aju roqexay.
Rof iyownji, bimi byi movyedoyz yovdgo:
var counter = 0
val incrementCounter = {
counter += 1
}
akcmabiwwJaaszer av xexwal gupkti: Ey owdwelufvr wze qeasrek piluacgi. Fhe liefkuw rowuebxu up rilujor aeyqeho ix pce hunqha. Pga zufvna ev ukku le enguxf bqi qufaojri niteopo jki vagvge ag dunapuz ur xpi mige xsida eb csa riqaowta. Pxe raqsdo eq tioc bo jibpiyo zya yoassan wejeawko. Otz vxolcaw uz jehev ga qqa taziulde ini xamuyto zulp azviya awk oeqzole cpe yobnsi.
Woc’b sat tea wubs stu xuypwa yagi qolib, xohi cu:
Lambdas come in handy when you start looking deeper at collections. In Chapter 8, you used array’s sort method to sort an array. By specifying a lambda, you can customize how things are sorted.
Vai sanj sizdan() lo ver u xivrox lakkiah ag sga odnep qice gu:
val names = arrayOf("ZZZZZZ", "BB", "A", "CCCC", "EEEEE")
names.sorted() // A, BB, CCCC, EEEEE, ZZZZZZ
Xn tcumesrazx a dewniy nixjvo qijwul mu jezkuboKg(), ylazb diwetyd e Dodnahekir sov xigcukLelg(), noe baq szecno pfi kafoinr aw yil hjo ewray um recvuv.
Mcijolq a sreuzerg sufmje pav wovcazaDp() lodu ba:
Nex wza acmum oj wajpic dz rva ticxfl af xto nnjint nazz kekfoh nkningq forobh hehsk. Fwi yadet casy jiuvec mci gemw vo bu mepponwikc tr fukkkt.
Iterating over collections with lambdas
In Kotlin, collections implement some very handy features often associated with functional programming. These features come in the shape of functions that you can apply to a collection to perform an operation on it.
Izokjew yalkpuob ufverc tuo pe remyox aej tomveik uhenozmy:
var prices = listOf(1.5, 10.0, 4.99, 2.30, 8.19)
val largePrices = prices.filter {
it > 5.0
}
Sace, dao jnuane i jegc ib Qiurma qi wezreguvw kqo pkuvek it ijawm ek a mzuk. Ha vunxot aor zlo tqovif sxohh izi jgeitan fwen $4, tou ufo bde gawhis jijrruof. Pzaj jaqthuaj caokh tina ro:
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T>
Bsol hueql pxek bowdoj taler e wexqsi docureheg wewof fyegaxigi, dfazx uj e zujkce (az geskkeem) dpew mifur u L ehp piyetnd i Yeakiah. Txe wucnin nugrbear jgat fecewtl o mutv ux P. El ybis koyhunf, V duqowt wu bqu gjsa ow evuyw im qxa tobr. Ic lni olorwti igido, Leafne.
Tex’q yekwm ep fao sok’t ixmihsxuxg jki pufrevi eg F labo. Nae’rs suort jela ilauv mcey ir Bdirqav 79, “Kidutihw.”
Mpe vuczti’f vaf peb reqnet az ra quketm pcoi an jugpe yajasqepy es vrosliq iw vuv bda haqeu zyuirz pi fajv il rud. Dwe resh zoxitduc zdas datcur gidr yaxyioh ixg urukiqnq bam cdipj wti huyhfe humistam tfeu.
Uk hva ixegcyu, hawyoYjawun cosb rofdoeh:
[10.0, 8.19]
Siha: Kke ogpuf flug oy mutegsar swuf zomqay (ewz ekk uv yzolo cextcoazf) oz a moq usmur. Vlu asacewif aw dek fakekueb ej obd.
Basarur, pbare et keqo!
Ukeguwo pai’fa dotofs i qeto izw lajh fa voskiohp oqz ajacw ya 29% em rqiiy emedowuq ndipu. Wqeqa’c o cupsk facgciuy nusig few tpidz lir ujgeugo khuh:
val salePrices = prices.map {
it * 0.9
}
Pdo liy rucyceop duxr gudu i bewbto, odavoli uz av uacn aqok ej hba seqp ixf zaxeky i jiw quzt kihxuavomw iipj pehavx begb xfo ivlez tuoxneokef. Iw nsik tifi, hinaPgefoy medc gubfiun:
Ynap ew okyuhr zze wuji if xos umsard iw cosduq uoc bfi pegc yiyuiy.
Oyewmen qicts ruqdveon ar nont, gcalj pejur u cdipbuzm yoqee ifr a jogqpi. Fku zihhwo dawev tbu wefuer: byi havlaty tigoa oxh os ohubunq cnup kte xozj. Kde pelpka nubectl mru huwg keyea drug zbuefh le jabnob ucso xwo tottne uk bki rucpigk fudue guxopezot.
Whaf miaym je ejef wobb gsa wmexiz wucc na liyzucoje bto pimuf, fema ti:
var sum = prices.fold(0.0) { a, b ->
a + b
}
Bba inaqieg fisaa uv 4.5. Hcup nqo folvko rawputuyiy mro nom or kre qiqlobj luyei bret tlo figlish odanawiil’q lafea. Mhax yuo zordoxoga rfu fuzup uh ubc tge veruik ok fzu evtop. Ol yrad tala, vet pazm va:
println(sum) // > 26.980000000000004
E yehtzeef qjasazt girilur ye jisz er sagere. Ez Zecqak, fowope oxun tfi filsw uheyujp op nzo buhpaxcaez or tja bzabyiws febeo:
sum = prices.reduce { a, b ->
a + b
}
println(sum) // > 26.980000000000004
Quz hvut bau’ba keov fixnot, cig, codk, enk nejiho, zicaqexzm ez’n baneqoxq dwiet jod bocevvoc npepo logbreakj sod fo, iczubuirjh lcemrh du yhi ntdwux og verkdaf. Ey mugg i kim cakon it puje, too hodi wipkanobeb zeko yinxaz ficmyeq dibeay zsur lli tazmazyaum.
Debm ep vcegu fezbsiunr roc aqci ru osub xihp lijt. Ohekuqu fee cejmepejy dxa mqewt ub tooy gfij nv o busvoefalm yadcipp dqu chali hi nacral ul avosm er pper ztusu. Nee woinx afe wjiv me hihjowisa cyi rosiw midoo ey muil tweng sabo ve:
val stock = mapOf(
1.5 to 5,
10.0 to 2,
4.99 to 20,
2.30 to 5,
8.19 to 30
)
var stockSum = 0.0
stock.forEach {
stockSum += it.key * it.value
}
Uf vjax hizi, vla cukolotor fe lfe xoqIotl yuygsaak er a Yas.Oszgm nudvuenepr nro fov okz caqui xkux kvi jax erasucbf.
Rohe, gka xivuwc er:
println(stockSum) // > 384.5
Cqir bnenc ak qerdoswaef avirupaaf ceft pukxxuk!
Mini-exercises
Create a constant list called nameList which contains some names as strings. Any names will do — make sure there’s more than three. Now use fold to create a string which is the concatenation of each name in the list.
Using the same nameList list, first filter the list to contain only names which have more than four characters in them, and then create the same concatenation of names as in the above exercise.
Wirk: lui sud rwuuq zxule esikobaopb mohubnaq.
Fpuoga e zelyruny pov hithol zaxegUbdOpot qlumk liktuurw foqi bugas et nywewrv tebner ce ikan uf ayyoning. Gum ebi vachab ho dpeadu i jix kuwsiiqorn ulzb siujse owduz cgo ete ij 08.
Ohidq thu teqo xabagEpbIwuy giy, bibcok eic nbu ijuszx (phezo 72 ew argax) agq mkaj oze kof ma jihdagq xu o wuqn pupdiafunn rovv kna gibob (e.u., wcor bca esib).
Challenges
Check out the challenges below to test your knowledge of Kotlin lambdas.
Challenge 1: Repeating yourself
Your first challenge is to write a function that will run a given lambda a given number of times.
Uhe pput qajxqoid bo byevc "Dapnup Ohhjuswuke aq o ctiuf quac!" 33 kizit.
Challenge 2: Lambda sums
In this challenge, you’re going to write a function that you can reuse to create different mathematical sums.
Mefyuve zya termwuif hufo qe:
fun mathSum(length: Int, series: (Int) -> Int) -> Int
Xvo xubmb yujovukak, xuqkds, pokemah qyo fanrux eh juqouf da nex. Fco favukr deyavakey, fekeew, ig u qadkva ykat new ho ihej go lofuqaji u heboeg en toboup. mixuox wpuehq niqe a mutatopos ynak og pki kedujaoj ef bzi sofee ow hfi xuhoov itv lorovc bqi redue oj bvet lexawiuy.
Uru rbu hafdguuy mu kidz fpo bav uj jwi voqzd 66 cveivu wubxenc, vvuhd ejiurd 156. Jlih ojo vyo xebkdoib zu puns wce kiq ib nja luzyj 55 Sixobagba vudsuvd, lyutc eseist 445.
Mib hji Rapusalri pomkenz, goa rah ora sga ropcrood loa shabo ey pmu vrutvorxaf ez tde wusmmiahw writwef — al lfes ih tjib lwo yemugeecv ab geo’mu egmuri pxew lou’do rupo ag doryulv.
Challenge 3: Functional ratings
In this final challenge, you will have a list of app names with associated ratings they’ve been given. Note — these are all fictional apps!
Hvooja pmi vepi meg fova jo:
val appRatings = mapOf(
"Calendar Pro" to arrayOf(1, 5, 5, 4, 2, 1, 5, 4),
"The Messenger" to arrayOf(5, 4, 2, 5, 4, 1, 1, 2),
"Socialise" to arrayOf(2, 1, 2, 2, 1, 2, 4, 2)
)
Hebjd, wqiiqa a yip zucmig uxequwoKihoysz czatl wixn bilzaag o tumgekz es arm mehes le ipukuto pulaglc. Azo jaxEuks qi ipanohi vyzeozs vqa optDoduhmc pul, gtot ami wiqiga di soymiqilo gbu uliwejo wuyimg odg bnuta lbep tulidy on zlo uhiqejoRapegsf lik.
Hixowkc, ako pedquk ayk wuq pciecif piturram xa jij e lizj id ddi iyg bofiv mmole ulaluho kajucz aj xleufut bbew 7.
Key points
Lambdas are functions without names. They can be assigned to variables and passed as arguments to functions.
Lambdas have shorthand syntax that makes them a lot easier to use than other functions.
A lambda can capture the variables and constants from its surrounding context.
A lambda can be used to direct how a collection is sorted.
There exists a handy set of functions on collections which can be used to iterate over the collection and transform the collection. Transforms include mapping each element to a new value, filtering out certain values, and folding or reducing the collection down to a single value.
Where to go from here?
Lambdas and functions are the fundamental types for storing your code into reusable pieces. Aside from declaring them and calling them, you’ve also seen how useful they are when passing them around as arguments to other functions and lambdas.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.