0% found this document useful (0 votes)
2 views

Lambda Functions

The document provides an in-depth overview of lambda functions in Kotlin, describing them as anonymous functions that can be treated as data types. It covers their syntax, characteristics, differences from regular functions, and various use cases, including higher-order functions, collection operations, and event handling. Additionally, it explains how lambda functions return the last expression automatically and discusses their internal mechanism and performance considerations.

Uploaded by

leviwom584
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views

Lambda Functions

The document provides an in-depth overview of lambda functions in Kotlin, describing them as anonymous functions that can be treated as data types. It covers their syntax, characteristics, differences from regular functions, and various use cases, including higher-order functions, collection operations, and event handling. Additionally, it explains how lambda functions return the last expression automatically and discusses their internal mechanism and performance considerations.

Uploaded by

leviwom584
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

Kotlin Lambda Functions - Guruprasad Hegde - Medium

Guruprasad Hegde

13 min read

Aug 27, 2024

What are Lambda functions in Kotlin?


In Kotlin, lambda functions (or simply lambdas) are anonymous functions that can be treated as a data type, meaning they
can be passed as arguments to other functions, returned from functions, or assigned to variables. They are a key feature in
Kotlin and are often used in functional programming and in conjunction with higher-order functions.

Like other data types that you can express with literal values — such as an Int type of a 31 value and a String type of a
"Lambda" value—you can also declare function literals, which are called lambda expressions or lambdas for short. You
use lambda expressions extensively in Android development and more generally in Kotlin programming.

Lambda expression syntax:


A basic lambda expression in Kotlin looks like this:

val sum = { a: Int, b: Int -> a + b }

Here’s a breakdown of this lambda:

• { a: Int, b: Int -> a + b }: This is the lambda expression.


• a: Int, b: Int: These are the parameters of the lambda.
• ->: This separates the parameters from the body of the lambda.
• a + b: This is the body of the lambda, which in this case, returns the sum of a and b.

A lambda expression in Kotlin is enclosed in curly braces {}. Inside the braces, you declare parameters (with optional
type annotations), followed by an arrow ->, and then the code body. If the return type isn’t Unit, the last expression in the
body is automatically treated as the return value.

Here are some key characteristics of lambdas:


• Anonymous: They don’t have a name like regular functions.
• Function literals: Defined without the fun keyword.
• Expressions: Passed as part of an expression, often as arguments to higher-order functions.
• Concise syntax: Uses curly braces {} to enclose the function body.

There are four main variations of function types, depending on whether they
take parameters and whether they return a value.
1. Function Type with Parameters and a Return Type
This type of function takes one or more parameters and returns a value.

Syntax:

(ParameterType1, ParameterType2, ...) -> ReturnType

Example:

val sum: (Int, Int) -> Int = { a, b -> a + b }

• Parameters: Two Int values (a and b).


• Return Type: Int (the result of adding a and b).

2. Function Type with Parameters and No Return Type (Unit)


This function type takes one or more parameters but does not return a value (Unit is the Kotlin equivalent of void in other
languages).

Syntax:

(ParameterType1, ParameterType2, ...) -> Unit

Example:

val printSum: (Int, Int) -> Unit = { a, b -> println("Sum: ${a + b}") }

• Parameters: Two Int values (a and b).


• Return Type: Unit (the function only prints the result)

3. Function Type with No Parameters and a Return Type


This function type doesn’t take any parameters but returns a value.

Syntax:

() -> ReturnType

Example:

val greet: () -> String = { "Hello, World!" }

• Parameters: None.
• Return Type: String (the function returns a greeting).

4. Function Type with No Parameters and No Return Type (Unit)


This is a function type that takes no parameters and returns nothing (Unit).

Syntax:

() -> Unit

Example:
val sayHello: () -> Unit = { println("Hello!") }

• Parameters: None.
• Return Type: Unit (the function only prints "Hello!").

it Keyword: Used for Single Parameter


In Kotlin, the it keyword is a shorthand way to refer to a single parameter in a lambda expression. When a lambda has
only one parameter, Kotlin automatically provides this parameter as it, so you don't have to explicitly declare it. This
makes the lambda more concise and readable.

1. Using it in Collection Operations

• Commonly used in operations like map, filter, forEach, etc.


• Example:

val numbers = listOf(1, 2, 3, 4, 5)


val doubled = numbers.map { it * 2 }
println(doubled)

Explanation: In this example, each element in the numbers list is referred to as it within the lambda, and it is multiplied
by 2.

2. Using it in Higher-Order Functions

• When passing a lambda as an argument, if the lambda has one parameter, it is used implicitly.
• Example:

fun performOperation(operation: (Int) -> Int): Int {


return operation(5)
}

val result = performOperation { it * 3 }


println(result)

Explanation: The lambda passed to performOperation takes a single Int parameter, which is referred to as it inside the
lambda. The lambda multiplies it by 3.

3. Using it in UI Configuration

• Common in Android development when setting up UI elements dynamically.


• Example

button.setOnClickListener {
Toast.makeText(context, "Button clicked!", Toast.LENGTH_SHORT).show()
}

Explanation: The setOnClickListener function takes a lambda with a single View parameter. The it keyword implicitly
refers to the clicked button.

When Not to Use 'it'


• Multiple Parameters: If your lambda has more than one parameter, you need to explicitly name the parameters,
and it cannot be used.

val sum = { a: Int, b: Int -> a + b }

• Explicit Naming for Clarity: Even with a single parameter, you might choose to explicitly name the parameter
instead of using it if it improves readability or clarity.

val printMessage = { message: String -> println(message) }

How do Lambda functions differ from regular functions in Kotlin


Lambda functions and regular functions in Kotlin serve similar purposes — they both encapsulate blocks of code that can
be executed later — but they have some key differences in terms of syntax, usage, and features.

1. Syntax

Regular Functions:

• Defined using the fun keyword, with a name, parameters, and a return type.
• Example

fun add(a: Int, b: Int): Int {


return a + b
}

Lambda Functions:

• Anonymous functions without a name, defined using curly braces {}.


• Parameters are declared within the braces, followed by the -> symbol, and the body of the lambda.
• Example

val add = { a: Int, b: Int -> a + b }

2. Usage Context

Regular Functions:

• Typically used when you need to define a reusable block of code that has a clear purpose and needs to be invoked
from multiple places.
• Can be top-level, member functions (inside a class or object), or local (inside another function).

Lambda Functions:

• Often used when you need a quick, inline function, especially when passing a function as an argument to a higher-
order function.
• They are more concise and are usually used in situations where defining a regular function would be overkill.

3. Naming

Regular Functions:

• Always have a name, which is used to invoke the function.


• Example:

val result = add(3, 4)

Lambda Functions:

• Anonymous and usually defined in place. They can be assigned to a variable, but the variable name is not the
function’s name.
• Example

val result = { a: Int, b: Int -> a + b }(3, 4)

4. Return Type

Regular Functions:

• Must specify a return type (either explicitly or inferred by the compiler).


• Example

fun multiply(a: Int, b: Int): Int {


return a * b
}

Lambda Functions:

• The return type is inferred from the expression in the body of the lambda. You don’t explicitly declare the return
type.
• Example

val multiply = { a: Int, b: Int -> a * b }

5. Receiver Type

Regular Functions:

• In regular functions, the receiver (if any) is declared in the function’s signature.
• Example

fun String.addExclamation(): String {


return this + "!"
}

Lambda Functions:

• Lambdas can have a receiver, making them extension-like functions, often used with Kotlin’s scope functions (let,
apply, run, etc.).
• Example

val addExclamation: String.() -> String = { this + "!" }

6. Return Behavior

Regular Functions:

• Can use the return keyword to exit the function and return a value.
• Example

fun divide(a: Int, b: Int): Int {


if (b == 0) return 0
return a / b
}

Lambda Functions:

• The return keyword inside a lambda expression refers to returning from the enclosing function, not just the
lambda. To return from the lambda itself, you can use a labeled return or avoid using return (by just letting the
lambda's last expression be the result).
• Example

val divide = { a: Int, b: Int ->


if (b == 0) 0 else a / b
}

7. Performance

• Regular Functions:

Compiled to regular methods, which can sometimes be slightly more optimized by the compiler.

• Lambda Functions:

May involve some overhead, especially when used heavily or in performance-critical code, due to their inline nature and
anonymous class creation.

• Regular functions are best for reusable, named pieces of code that have a specific purpose.
• Lambda functions are great for concise, inline code blocks, especially when working with higher-order functions
and functional programming paradigms in Kotlin.

Use cases of Lambda Function:


Lambda functions in Kotlin have a wide range of use cases, particularly in scenarios where you need to pass behavior as a
parameter, work with collections, or streamline repetitive operations. Here are some common use cases
1. Higher-Order Functions

• Passing Behavior as a Parameter: Lambda functions are often used as arguments to higher-order functions,
allowing you to pass behavior (like transformations, filtering, or processing logic) as a parameter.
• Example: Sorting a list of objects by a specific attribute.

data class Person(val name: String, val age: Int)


val people = listOf(Person("Alice", 30), Person("Bob", 25))
val sortedByAge = people.sortedBy { it.age }
println(sortedByAge)

2. Collection operations

• Mapping, Filtering, Reducing: Lambdas are commonly used with Kotlin’s collection functions like map, filter,
reduce, forEach, etc., to perform operations on collections.
• Example: Filtering even numbers from a list

val numbers = listOf(1, 2, 3, 4, 5, 6)


val evenNumbers = numbers.filter { it % 2 == 0 }
println(evenNumbers)

3. Scope Functions

• let, apply, run, with, also: Scope functions use lambdas to operate on objects within a specific scope, often used
to initialize objects, perform operations, or handle nullability.
• Example: Initializing and configuring an object

val person = Person("John", 25).apply {


println("Person's name is $name and age is $age")
}

4. Event Handling

• Callbacks: Lambdas are useful for handling events, such as button clicks in Android development, where you pass
a lambda to handle the event.
• Example: Handling a button click in Android

button.setOnClickListener {
println("Button clicked!")
}

6. Asynchronous Programming

• Callbacks and Continuations: Lambdas are commonly used to handle asynchronous operations, such as making
network requests or processing data in the background.
• Example: Handling the result of an asynchronous operation

fun fetchData(callback: (String) -> Unit) {

callback("Data fetched")
}

fetchData { result ->


println(result)
}

7. Inline Functions and Reusability

• Code Reusability: Lambdas help create reusable blocks of code that can be passed around and executed in
different contexts, often used with inline functions to reduce overhead.
• Example: Creating a reusable logging function

inline fun logExecution(action: () -> Unit) {


println("Action started")
action()
println("Action finished")
}
logExecution {
println("Performing the action")
}

9. Extension Functions

• Adding Behavior to Existing Classes: You can use lambdas with receiver types to add new functionality to
existing classes, making the code more expressive and concise.
• Example: Adding a custom operation to a list

fun List<Int>.sumOfSquares(): Int {


return this.sumOf { it * it }
}

val result = listOf(1, 2, 3).sumOfSquares()


println(result)

10. UI Development

• Adding Behavior to Existing Classes: You can use lambdas with receiver types to add new functionality to
existing classes, making the code more expressive and concise.
• Example: Adding a custom operation to a list

val textView = TextView(context).apply {


text = "Hello, Kotlin!"
textSize = 16f
}

How lambda function return the last statement and how does it work
internally?
In Kotlin, lambda functions automatically return the value of their last expression, which simplifies their
syntax and usage. This behavior is a key feature of lambda expressions and is rooted in how Kotlin handles
expressions and returns in lambdas.

How Lambda Functions Return the Last Statement


In a lambda function, you don’t need to explicitly use the return keyword to return a value. The value of the last
expression in the lambda's body is returned automatically. Here’s how it works

val sum = { a: Int, b: Int -> a + b }

{ a: Int, b: Int -> a + b }:

• a + b is the last (and only) expression in this lambda.


• The result of a + b is automatically returned when the lambda is invoked

Example with Multiple Statements


If the lambda has multiple statements, the last one is returned:

val processNumbers = { a: Int, b: Int ->


val sum = a + b
val product = a * b
product
}

val result = processNumbers(3, 4)

product: This is the last expression in the lambda, so its value (which is a * b) is returned
Internal Mechanism: How It Works
Internally, Kotlin compiles lambda expressions into instances of anonymous classes that implement functional interfaces
(like FunctionN, where N is the number of parameters). The return value of the lambda is determined by the value of the
last expression, which becomes the return value of the invoke method of this anonymous class.

Consider the following lambda

val add = { a: Int, b: Int -> a + b }

Internally, it might look something like this (simplified for understanding):

val add = object : Function2<Int, Int, Int> {


override fun invoke(a: Int, b: Int): Int {
return a + b
}
}

1. Lambda as an Anonymous Class:

• When a lambda is defined, Kotlin creates an anonymous class that implements the corresponding functional
interface.
• For example, the lambda { a: Int, b: Int -> a + b } is compiled to an anonymous class that implements the
Function2<Int, Int, Int> interface.

2. Invoke Method:

• This anonymous class has an invoke method that takes the lambda’s parameters as arguments.
• The body of the lambda is executed inside this invoke method.

3. Returning the Last Expression:

• During the execution of the invoke method, the last expression in the lambda’s body is evaluated, and its result is
returned by the invoke method.
• Kotlin does this automatically, so you don’t have to explicitly specify a return statement

When you invoke add(3, 4), Kotlin calls the invoke method on this anonymous class, passing 3 and 4 as arguments. The
invokemethod executes a + b and returns the result.

Performance Consideration
Kotlin uses inlining for lambdas passed to higher-order functions marked with the inline keyword. When a
lambda is inlined, its code is directly inserted at the call site, avoiding the overhead of creating an
anonymous class.

For non-inlined lambdas, the anonymous class approach is used, which involves a small amount of overhead
due to object creation and method invocation.

Non-Technical Example: Ordering Coffee


Imagine you’re in a coffee shop, and you have a friend who is really good at remembering and placing orders. Instead of
giving them specific instructions on how to order each time, you give them a general task to handle the ordering for you.
You tell them, “Whenever I want a coffee, just order it how I like it.”

In Kotlin, this would be like defining a lambda function that takes care of ordering your coffee.

1. Defining the Task (Lambda Function)

• You tell your friend, “Whenever I say I want a coffee, just order a cappuccino with extra foam.”
• In Kotlin, this is like defining a lambda

val orderCoffee = { println("Ordering a cappuccino with extra foam!") }

2. Using the Task (Calling the Lambda):


• When you’re ready for coffee, you simply tell your friend, “Order my coffee.”
• In Kotlin, you would call the lambda

orderCoffee()

3. Handling Different Orders (Lambda with Parameters)

• Maybe sometimes you want a different kind of coffee. So, you modify your instruction: “Whenever I say I want
coffee, just ask me what kind, and then order it.”
• In Kotlin, this is like a lambda with parameters

val orderCoffee = { type: String -> println("Ordering a $type!") }

• Now, when you tell your friend, “Order my coffee,” you can specify what kind you want

orderCoffee("latte")
orderCoffee("espresso")

4. Getting a Result (Returning a Value)

• Suppose you want to know the price of the coffee after your friend orders it. You could instruct them, “Order my
coffee, and then tell me the price.”
• In Kotlin, the lambda could return a value

val orderCoffee = { type: String ->


println("Ordering a $type!")
5.0
}

val price = orderCoffee("cappuccino")


println("The price is $$price")

Understanding the concept of Coffee


• Lambda Functions: Think of them as simple instructions or tasks that you can give to someone (or your program)
to carry out. You can define them once, and then reuse them whenever you need that task done.
• Parameters: If you need some flexibility, you can ask for specific details (like what type of coffee), just like how
you can pass parameters to a lambda.
• Returning Values: Just like your friend could tell you the price of the coffee after ordering, a lambda can return a
value after doing its work.

Anonymous Function
Anonymous functions in Kotlin are similar to lambda expressions but provide additional flexibility, especially when it
comes to specifying return types and using return statements. While lambda expressions are generally more concise,
anonymous functions offer features that are useful in certain scenarios.

The body of the anonymous function can be either an expression or block.

1. Function body as an expression

fun(a: Int, b: Int) : Int = a * b

2. Function body as a block

fun(a: Int, b: Int): Int {


val mul = a * b
return mul
}

Key Characteristics of Anonymous Functions


1. Syntax :
• An anonymous function is defined using the fun keyword, followed by the function's parameters, return type, and
body.
• Example

val multiply = fun(a: Int, b: Int): Int {


return a * b
}
println(multiply(2, 3))

2. Return Types

• Unlike lambdas, you can explicitly specify the return type of an anonymous function. This is particularly useful
when the type cannot be inferred or when you want to make the return type clear.
• Example

val add = fun(a: Int, b: Int): Int {


return a + b
}

3. Usage of return

• In an anonymous function, the return keyword behaves as it would in a regular function, returning from the
anonymous function itself rather than the enclosing function. This contrasts with lambdas, where return can be
non-local if used in an inline function.
• Example

fun doOperation(x: Int, operation: (Int) -> Int): Int {


return operation(x)
}

val result = doOperation(5, fun(x: Int): Int {


return if (x > 0) x * 2 else x
})
println(result)

4. No Implicit it

• Unlike lambdas, anonymous functions do not have an implicit it parameter for single-parameter functions. You
need to explicitly define the parameter name.
• Example

val isEven = fun(num: Int): Boolean {


return num % 2 == 0
}
println(isEven(4))

5. When to Use Anonymous Functions

• Control Flow with return: If you need to return from the function explicitly, especially within loops or other
complex structures, anonymous functions are more appropriate.
• Explicit Return Type: When you need to clearly define or specify the return type of a function, an anonymous
function can be more readable.
• Multiple Lines of Code: For longer or more complex logic where a lambda might become hard to read, an
anonymous function can be clearer.

6. Comparison with Lambdas

• Lambdas: More concise, better suited for short and simple functions, can use implicit it for single parameters, and
can have non-local returns in inline functions.
• Anonymous Functions: More flexible with control over return types and explicit return, no implicit it, and better
for more complex logic.

Happy coding !!

You might also like