Chapter 5: Programming with lambdas
The chapter introduces lambdas as passable chunks of code that help make Kotlin code reusable and expressive.
After this chapter you will be able to
- Lambdas as reusable behavior. Explain that a lambda is a small chunk of code that can be passed to a function, allowing common code structures to be extracted into reusable library functions.
- Lambdas as behavior values. Explain how a lambda lets Kotlin code pass a block of behavior as a function parameter instead of declaring a class and passing an instance. Describe why this is useful in the button-click listener example.
- Selecting a maximum by age. Explain how maxByOrNull uses { it.age } to find the oldest Person in a collection, and how a member reference can express the same selector.
- Lambda structure and result. Describe how a Kotlin lambda is written, used as a value, and evaluated when it contains multiple statements.
- Trailing lambda placement. Explain when a lambda argument can be placed outside a call’s parentheses, when the parentheses can be omitted, and the readability trade-off.
- Inferred parameters and default it. Explain when lambda parameter types may be omitted, when a stored lambda needs explicit types, and when the default name it is available or best avoided.
- Access to surrounding variables. Explain that a lambda can use function parameters and local variables declared before it, and can modify mutable local variables. Describe such external variables as captured by the lambda.
- Timing of asynchronous changes. Explain why changing a captured local variable in a later-running event handler does not make the change observable before the enclosing function returns, and where the example says to keep the value instead.
- Member reference syntax and use. Explain how the :: operator creates a value that refers to a method or property, and how that value can replace a lambda that calls or accesses it.
- Bound versus regular member references. Distinguish a regular member reference from a bound callable reference using the Person age example, and explain how seb::age relates to the lambda { seb.age }.
- Lambdas for Java listeners. Explain why a Kotlin lambda can be passed to Java’s setOnClickListener method and how its parameter relates to the OnClickListener method.
- Automatic Runnable conversion. Explain how a lambda passed to a Java method expecting Runnable becomes an implementation of that interface.
- Runnable instance reuse and capture. Distinguish the instance created by an explicit anonymous object from one created for a lambda, and describe how capturing a surrounding variable changes reuse.
- Explicit SAM constructor conversion. Explain what a SAM constructor takes and returns, and describe when to use it instead of relying on automatic SAM conversion.
- Fun interface contracts. Explain that a Kotlin fun interface has exactly one abstract method but may also have non-abstract methods, making it useful for contracts that a function type’s signature cannot express.
- Lambda implementations of fun interfaces. Describe how a lambda can implement a fun interface’s abstract method, either through a SAM constructor or when passed directly to a function expecting that interface.
- Unqualified calls in receiver lambdas. Explain that a lambda with a receiver can call methods of another object in its body without additional qualifiers, a capability the section contrasts with Java.
- The with receiver. Explain that `with` is a library function taking an object and a lambda, and that it makes the object the lambda’s receiver so its members can be called without repeating the object’s name.
- Apply receiver and return value. Explain how apply uses the object it is called on as the lambda’s receiver and returns that same object, unlike with. Describe how this lets a function create, configure, and return an initialized object.
- Also argument and return behavior. Explain how also gives its lambda the original object as an argument and returns that same object, making it useful for additional actions during a chain of operations.
What it builds on
- Object declarations: Singletons made easy (chapter 4) · The listener example contrasts a lambda with an implementation using an object declaration.
- Combining functional and object-oriented programming makes Kotlin safe and flexible (chapter 1) · The earlier discussion introduces Kotlin's combination of functional and object-oriented programming.
- Declaring functions with parameters and return values (chapter 2) · Familiarity with function parameters and return values.
- Named arguments (chapter 3) · Named arguments provide an alternative that can clarify a lambda’s purpose.
- Declaring variables to store data (chapter 2) · Local-variable type inference provides a comparison for omitting explicit types.
- Marking a variable as read only or reassignable (chapter 2) · The distinction between read-only val and reassignable var.
- Getting rid of static utility classes: Top-level functions and properties (chapter 3) · Top-level functions are not members of a class.
- Adding methods to other people’s classes: Extension functions and properties (chapter 3) · Extension functions can be called with member-like syntax.
Section by section
Introduction
The chapter introduces lambdas as passable chunks of code that help make Kotlin code reusable and expressive.
- Lambdas as reusable behavior: Explain that a lambda is a small chunk of code that can be passed to a function, allowing common code structures to be extracted into reusable library functions.
Why it matters: This is the basic reason lambdas are useful throughout Kotlin’s standard library.
Key terms: lambda expressions, lambdas, library functions
5.1 Lambda expressions and member references
This introduction notes the anticipation surrounding Java 8 lambdas and previews a discussion of their usefulness and Kotlin syntax.
5.1.1 Introduction to lambdas: Blocks of code as values
Introduces lambdas as a concise way to pass behavior as a value, using a button-click listener to contrast them with an object implementation.
- Lambdas as behavior values: Explain how a lambda lets Kotlin code pass a block of behavior as a function parameter instead of declaring a class and passing an instance. Describe why this is useful in the button-click listener example.
Why it matters: It explains why lambdas make callbacks and other behavior-passing code easier to read. - Functional programming hallmarks: Describe the functional-programming ideas refreshed here: first-class functions, immutability, and pure functions without side effects.
Why it matters: These ideas place lambdas within the broader functional-programming approach described in the section.
Key terms: lambda expressions, functions as values, OnClickListener, onClick, first-class functions, immutability, no side effects, pure
5.1.2 Lambdas and collections
This section shows how a lambda-based collection function can replace a manual search for the oldest person.
- Selecting a maximum by age: Explain how maxByOrNull uses { it.age } to find the oldest Person in a collection, and how a member reference can express the same selector.
Why it matters: You can express what makes an element the maximum without writing the search loop yourself. - Library functions versus manual loops: Justify using collection functions with lambdas instead of writing a manual search loop in this example.
Why it matters: Recognizing a common collection task lets you choose code that is shorter and clearer about its purpose.
Key terms: maxByOrNull, lambda, selector, it, member reference, collections, lambdas, standard library, loop-based counterpart
5.1.3 Syntax for lambda expressions
This section explains Kotlin lambda syntax and how to make lambda expressions concise while keeping them readable.
- Lambda structure and result: Describe how a Kotlin lambda is written, used as a value, and evaluated when it contains multiple statements.
Why it matters: This lets you recognize what a lambda receives and what it produces. - Trailing lambda placement: Explain when a lambda argument can be placed outside a call’s parentheses, when the parentheses can be omitted, and the readability trade-off.
Why it matters: Lambda placement affects both the concision and clarity of Kotlin calls. - Inferred parameters and default it: Explain when lambda parameter types may be omitted, when a stored lambda needs explicit types, and when the default name it is available or best avoided.
Why it matters: These shortcuts make lambdas shorter without sacrificing clarity about their parameters. - Run for expression blocks: Describe why the section suggests run instead of directly calling a lambda when a block of statements is needed as an expression.
Why it matters: It gives a readable way to put a multi-statement block where Kotlin expects an expression.
Key terms: lambda expression, parameters, body, last expression, last argument, named argument, parentheses, lambda parameter type, type inference, it
5.1.4 Accessing variables in scope
This section explains how Kotlin lambdas access and modify surrounding variables, how captured variables are retained, and why execution timing matters.
- Access to surrounding variables: Explain that a lambda can use function parameters and local variables declared before it, and can modify mutable local variables. Describe such external variables as captured by the lambda.
Why it matters: This explains how a lambda can use context from the function where it is written. - Storage of captured values: Explain how capturing lets a lambda retain access to a local variable for later execution, distinguishing storage of a val’s value from the wrapper used for a mutable var.
Why it matters: The storage distinction explains why a captured var can still be changed when the lambda runs. - Timing of asynchronous changes: Explain why changing a captured local variable in a later-running event handler does not make the change observable before the enclosing function returns, and where the example says to keep the value instead.
Why it matters: It prevents a mistaken assumption that registering a callback has already run its code.
Key terms: lambda, captured, local variables, parameters, val, var, Ref class, event handler, onClick handler, local variable
5.1.5 Member references
This section shows how Kotlin member references turn existing functions, properties, and constructors into values that can be used in place of lambdas.
- Member reference syntax and use: Explain how the :: operator creates a value that refers to a method or property, and how that value can replace a lambda that calls or accesses it.
Why it matters: You can pass existing behavior without writing a redundant lambda around it. - Top-level and extension references: Describe how references to top-level functions and extension functions fit the member-reference syntax.
Why it matters: The reference syntax also works for functions that are not declared as class members. - Deferred constructor reference: Explain what a constructor reference represents and how its syntax is formed.
Why it matters: Object creation can be passed around as an action rather than performed immediately.
Key terms: member reference, :: operator, function value, top-level function, extension function, ::salute, constructor reference
5.1.6 Bound callable references
This subsection explains how a bound callable reference captures a member of a specific object, unlike a regular member reference that takes an object as an argument.
- Bound versus regular member references: Distinguish a regular member reference from a bound callable reference using the Person age example, and explain how seb::age relates to the lambda { seb.age }.
Why it matters: The distinction tells you whether a callable reference still needs an object passed to it or already has one captured.
Key terms: bound callable references, regular member reference, seb::age
5.2 Using Java functional interfaces: Single abstract methods
This section explains why Kotlin can pass a lambda to a Java method that expects a functional interface, using a button click listener as the example.
- Lambdas for Java listeners: Explain why a Kotlin lambda can be passed to Java’s setOnClickListener method and how its parameter relates to the OnClickListener method.
Why it matters: It lets you use Java APIs from Kotlin without writing an anonymous listener class.
Key terms: functional interface, single abstract method (SAM) interface, OnClickListener, onClick, lambda
5.2.1 Passing a lambda as a parameter to a Java method
This subsection explains how Kotlin passes lambdas to Java methods expecting functional interfaces, when the resulting object can be reused, and why this account differs for inline Kotlin functions.
- Automatic Runnable conversion: Explain how a lambda passed to a Java method expecting Runnable becomes an implementation of that interface.
Why it matters: This explains why a Kotlin lambda can be supplied where a Java method expects a functional interface. - Runnable instance reuse and capture: Distinguish the instance created by an explicit anonymous object from one created for a lambda, and describe how capturing a surrounding variable changes reuse.
Why it matters: Whether a lambda captures local values determines whether its generated object can be shared across calls. - Inline Kotlin function exception: Distinguish the Java functional-interface case from passing a lambda to an inline Kotlin function.
Why it matters: It prevents applying the Java interop explanation to every Kotlin lambda call.
Key terms: lambda, functional interface, Runnable, single abstract method, run, anonymous object, captured variables, anonymous class instance, Java methods, functional interfaces
5.2.2 SAM constructors: Explicit conversion of lambdas to functional interfaces
This section explains how SAM constructors explicitly convert lambdas to functional-interface instances and when an anonymous object is needed instead.
- Explicit SAM constructor conversion: Explain what a SAM constructor takes and returns, and describe when to use it instead of relying on automatic SAM conversion.
Why it matters: It lets you produce a functional-interface instance explicitly when a bare lambda is not enough. - Listener self reference limits: Distinguish a lambda listener from an anonymous-object listener when the listener must remove itself.
Why it matters: The choice between a lambda and an anonymous object matters when a listener needs to unsubscribe itself.
Key terms: SAM constructor, functional interface, single abstract method, SAM conversion, lambda, this, anonymous object, event listener
5.3 Defining SAM interfaces in Kotlin: fun interfaces
This section explains when to define a Kotlin fun interface, how to supply its implementation with a lambda, and why it can make Java call sites cleaner.
- Fun interface contracts: Explain that a Kotlin fun interface has exactly one abstract method but may also have non-abstract methods, making it useful for contracts that a function type’s signature cannot express.
Why it matters: This distinction helps you choose between a simple function type and a named interface with a richer contract. - Lambda implementations of fun interfaces: Describe how a lambda can implement a fun interface’s abstract method, either through a SAM constructor or when passed directly to a function expecting that interface.
Why it matters: You can recognize why a lambda is accepted even when a function parameter is declared as an interface. - Cleaner Java lambda calls: Explain why a fun interface can make a Kotlin API easier to call from Java than a Kotlin function type when the operation returns nothing.
Why it matters: The choice of parameter type can affect how concise your API is for Java callers.
Key terms: fun interface, functional interface, function type, abstract method, non-abstract method, SAM constructor, lambda, IntCondition, Java call site, Kotlin function type
5.4 Lambdas with receivers: with, apply, and also
Introduces lambdas with receivers as a Kotlin feature that lets a lambda call another object’s methods without additional qualifiers.
- Unqualified calls in receiver lambdas: Explain that a lambda with a receiver can call methods of another object in its body without additional qualifiers, a capability the section contrasts with Java.
Why it matters: This idea prepares you to recognize what is distinctive about the lambda used by with.
Key terms: lambdas with receivers, qualifiers
5.4.1 Performing multiple operations on the same object: with
This section shows how `with` lets a lambda perform several operations on one object without repeatedly naming it, and explains what the call returns.
- The with receiver: Explain that `with` is a library function taking an object and a lambda, and that it makes the object the lambda’s receiver so its members can be called without repeating the object’s name.
Why it matters: This is why `with` can make repeated operations on the same object more concise. - Receiver lambda analogy: Describe how a lambda with a receiver resembles an extension function in its access to the receiver.
Why it matters: The extension-function analogy gives you a familiar way to understand the lambda’s implicit `this`. - With results and name conflicts: State what a `with` call returns and describe how an explicit label can distinguish an outer class’s method from a same-named method on the receiver.
Why it matters: These details help you tell which value a `with` expression produces and which object a method call targets.
Key terms: with, lambda with a receiver, receiver, this, extension function, last expression, explicit label
5.4.2 Initializing and configuring objects: The apply function
Explains how apply configures an object through a receiver lambda and returns that object, then introduces specialized builder functions that use the same pattern.
- Apply receiver and return value: Explain how apply uses the object it is called on as the lambda’s receiver and returns that same object, unlike with. Describe how this lets a function create, configure, and return an initialized object.
Why it matters: This makes it possible to configure a newly created object and return it in one expression. - Specialized receiver-based builders: Describe how buildString simplifies constructing a String with StringBuilder, and how collection builder functions allow mutation during construction while producing read-only collections.
Why it matters: These builders apply the receiver-lambda pattern to common construction tasks without requiring you to manage every setup step.
Key terms: apply, receiver, lambda with a receiver, with, buildString, StringBuilder, buildList, buildSet, buildMap
5.4.3 Performing additional actions with an object: also
This section explains how also performs an additional action with an object while returning that object for continued use.
- Also argument and return behavior: Explain how also gives its lambda the original object as an argument and returns that same object, making it useful for additional actions during a chain of operations.
Why it matters: It helps you read a chain where code does something extra with an object without replacing the object being passed along.
Key terms: also, apply, it, receiver object
5.5 Summary
Where this is used later
- Function types specify the parameter types and return values of a lambda (chapter 10) · Function types formalize the parameter and return types of behavior passed as a value.
- Functional APIs for collections (chapter 6) · Builds on using library functions with lambdas to work with collections.
- (chapter intro) (chapter 6) · Introduces using lambdas with collections.
- Removing and transforming elements: filter and map (chapter 6) · Using lambdas with collections.
- Removing the overhead of lambdas with inline functions (chapter 10) · Introduces lambdas that access variables in scope.
- Converting a list to a map of groups: groupBy (chapter 6) · The syntax and idea of member references.
- The Kotlin reflection API: KClass, KCallable, KFunction, and KProperty (chapter 12) · Introduces the ::foo member-reference syntax.
- Java lambdas are automatically converted to Kotlin function types (chapter 10) · Passing Kotlin lambdas to Java methods through SAM conversion
Concepts explained in this chapter
- lambdaA function literal is source syntax that directly expresses a function value, including its parameters and body.
- functional interfaceA functional interface is an interface with exactly one abstract method, allowing it to be implemented by a function value or lambda in compatible contexts.
- lambda with receiverA lambda with receiver is a lambda whose function type designates one parameter as an implicit receiver, allowing the lambda body to access that receiver's members without qualification.
- implicit lambda parameterAn implicit lambda parameter is the automatically generated name `it` for the sole parameter of a Kotlin lambda when its type can be inferred.
- applyapply is a Kotlin standard-library scope function that executes a lambda with the receiver as its implicit receiver and returns that receiver.
- member referenceA member reference is a Kotlin expression that refers to a class or object member as a callable value.
- trailing lambdaA trailing lambda is a Kotlin lambda argument written outside a function call's parentheses when it is the call's last argument.
- captured variableA captured variable is a variable that stores the subject of a Kotlin when expression and is accessible within that expression's branches.
- withwith is a Kotlin standard-library scope function that executes a block with an object as its receiver and returns the block result.
- loopA loop is a control structure that repeatedly executes a block of code according to a condition or over a sequence of values.
- first-class objectA first-class function is a function that can be stored in variables, passed as an argument, or returned from another function.
- functional, stateless philosophyFunctional programming treats computation primarily as the evaluation and composition of functions, often emphasizing immutable data and limited side effects.
- interopJava interoperability is the ability of Kotlin code to interact directly with Java code and libraries, including exchanging compatible string values without conversion.
- immutable collectionImmutability is the property that an object's state cannot change after the object is created.
- pure functionA pure function is a function whose output is determined solely by its explicit inputs and whose evaluation has no externally observable side effects.
- object expressionAn object expression is Kotlin syntax that creates an anonymous object, optionally implementing a class or interfaces, at the point where it is written.
These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.
