Functional Interfaces and SAM Conversion
Functional Interfaces and SAM Conversion
Kotlin functional interfaces, also called Single Abstract Method (SAM) interfaces, allow an interface with exactly one abstract method to be instantiated using a lambda expression. This mechanism reduces boilerplate when defining callbacks, listeners, and strategy patterns. SAM conversion is also the bridge that lets Kotlin code pass lambdas directly to Java APIs that expect single method interfaces. By the end of this lesson, you will be able to:
- Define a functional interface in Kotlin using the
fun interfacedeclaration. - Explain how SAM conversion transforms a lambda into an interface implementation.
- Describe the difference between Kotlin
fun interfaceand a regular interface with one method. - Identify when SAM conversion applies to Java interfaces used from Kotlin.
- Recognize the bytecode implications of SAM conversion versus explicit object expressions.
Declaring a Functional Interface
A functional interface in Kotlin is declared with the fun interface modifier. This tells the compiler that the interface is eligible for SAM conversion. The interface must contain exactly one abstract method, though it may include non-abstract members such as default methods or properties:
fun interface Transformer<T, R> {
fun transform(value: T): R
}
val intToString = Transformer<Int, String> { value ->
"Number: $value"
}
println(intToString.transform(42)) // Number: 42
Without the fun modifier, the same interface would require an explicit object expression to create an instance. The fun keyword is the signal that enables the compiler to accept a lambda in place of the full interface implementation.
SAM Conversion Mechanics
When the compiler encounters a lambda assigned to a functional interface type, it generates an anonymous class that implements the interface and delegates the abstract method to the lambda body. This is SAM conversion. The conversion happens at compile time, and the generated class is visible in the bytecode:
fun interface ClickHandler {
fun onClick(id: Int)
}
fun registerHandler(handler: ClickHandler) {
handler.onClick(1)
}
// SAM conversion: lambda becomes a ClickHandler instance
registerHandler { id ->
println("Clicked item $id")
}
The compiler generates roughly the equivalent of:
registerHandler(object : ClickHandler {
override fun onClick(id: Int) {
println("Clicked item $id")
}
})
The key difference is that the lambda syntax is more concise and the compiler may optimize the generated class. On the JVM, the compiler can use invokedynamic and LambdaMetafactory to defer class generation to runtime, which reduces the number of classes in the compiled output.
This interview continues for subscribers
Subscribe to Dove Letter for full access to exclusive interviews about Android and Kotlin development.
Become a Sponsor