Idiomatic Kotlin: Lambdas and SAM constructors

Tompee Balauag
Jul 11, 2018 · 5 min read
Photo by Nathan Dumlao on Unsplash

This article is a part of the Idiomatic Kotlin series. The complete list is at the bottom of the article.

In this article we will explore Kotlin lambdas, SAM constructors and interoperability with Java functional interfaces. For those who are not familiar with functional programming, lambdas and functional interfaces in Java, this might not be the best entry point for you but I will try to explain as easy as possible.

What is a Lambda expression?

A lambda expression is a block of code that can be passed to another function. Functions that accept these types of functions are normally called Higher-order functions. Lambda expressions allows us to realize Functional Programming, a paradigm wherein functions are treated as values.

Before Java 8, this is achieved using anonymous inner classes. Let us look at an example.

To achieve behavior passing, you need to create an instance of the interface and pass it to the function.

Java 8 introduced lambda expressions along with functional interfaces. A functional interface is an interface with a single method. They are commonly referred to as single abstract method or SAM. Lambda expressions represents these functional interfaces in a more concise way to deal with functional programming. Converting the above code to lambda syntax will look like

Plain and simple. Kotlin has a similar syntax for lambdas. We will discuss that in the next sections.

Motivation

Aside from conciseness, syntactic sugar and reduction of boilerplate code, there are other benefits to using lambdas as well. Lambda functions, if not capturing, each exists as a single object in the JVM and reusing them will not incur any other performance overhead. They are also generated at runtime while anonymous inner classes are class-loaded and instantiated for each call.

How to define a lambda function?

The most concise syntax for a kotlin lambda function is

{ param1 : Type1, param2 : Type2 -> body }

Specific rules to follow:

  1. Always enclosed in curly braces
  2. Left part consists of comma separated parameters. No need to enclose in parentheses. The arrow serves as the separator.
  3. The right side is the code body. This can be multi-line and the last expression can be the return value. An explicit return@<operation> can also be used.

Lambda functions can be used directly or assigned to a variable. When a lambda expression is stored in a variable, you can invoke it as a regular function or using the invoke method. A direct lambda function can be run as a function as well, but is does not offer good readability.

A lambda can be passed to a function in a lot of ways. If the function requires a lambda as its final parameter, you can take it out of the parentheses. Other than that you use the general syntax of passing arguments.

There is another simplification in lambda syntax that I myself is fond of using. If the function has a single argument and its type can be inferred, an autogenerated variable named it will be available for you to use. This is handy on cases wherein you are running out of variable names, or names does not matter much but is not for cases of nested lambdas.

Differences of Java and Kotlin lambdas

Java lambdas can only access final variables. Kotlin on the other hand supports access of non-final variables. This case was briefly touched in another one of my articles. Kotlin supports this feature by wrapping the variables in a reference and the lambda function captures this reference for it to use.

Compatibility with Java Functional Interfaces

Kotlin lambdas are fully compatible with Java functional interfaces. To prove it let us decompile a simple setOnTouchListener call.

The compiler is smart enough to know that the function needs an instance of View.OnTouchListener. The compiler interprets a lambda function that represents a functional interface as a instance of an anonymous class implementing that functional interface.

You may think that Kotlin just converts the lambda into an anonymous class just like the old times. Well there is a bit of a catch here. When using anonymous classes explicitly, you are always creating a new instance. But in lambda, if it is not capturing, there will exist a single instance of it that is used on every reuse.

Note: For those who are not familiar, capturing lambdas are lambdas that use variables that are outside their immediate scope. Think of it as a function in a class that uses private fields. You can say it “captures” those variables. Non-capturing on the other hand does not depend on any variable outside their immediate scope. You can think of them as pure functions.

SAM Constructors

Even though the compiler is smart enough to convert your lambda into an anonymous class implementation of functional interfaces, there are still cases wherein you need to explicitly convert. Kotlin supports explicit conversion using the SAM constructors.

SAM constructors allow you to convert a lambda expression to an instance of a function interface. The syntax is pretty straightforward.

FunctionalInterfaceName { lambda_function }

When do you need to do this you say? Let’s take a look at 2 examples.

Both of this cases requires you to have a specific functional interface type. Lambda cannot be used directly for that.

Java SAM vs. Kotlin Function Types

Kotlin uses function types instead of interfaces. So if you are coding in pure Kotlin, it is important to use function types for lambda types as Kotlin does not support conversion of lambdas to Kotlin interfaces. More on function types soon.

Considerations

Lambdas are not without their overhead. A lambda function by default, under the hood, generates .class and surprisingly, a step towards reaching your DEX limit. There is a good summary here. Thankfully, we can reduce some of these overhead using other features such as inlining. This will be discussed in one of the future articles so watch out for it.

Check out the other articles in the idiomatic kotlin series. The sample source code for each article can be found here in Github.

  1. Extension Functions
  2. Sealed Classes
  3. Infix Functions
  4. Class Delegation
  5. Local functions
  6. Object and Singleton
  7. Sequences
  8. Lambdas and SAM constructors
  9. Lambdas with Receiver and DSL
  10. Elvis operator
  11. Property Delegates and Lazy
  12. Higher-order functions and Function Types
  13. Inline functions
  14. Lambdas and Control Flows
  15. Reified Parameters
  16. Noinline and Crossinline
  17. Variance
  18. Annotations and Reflection
  19. Annotation Processor and Code Generation

Familiar Android

Catching up with the latest in Android development

Tompee Balauag

Written by

Android developer, gamer

Familiar Android

Catching up with the latest in Android development

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade