How do Kotlin function types relate to Java functional (SAM) interfaces, and when does SAM conversion apply?
answer
- SAM = single abstract method interface
- Java SAM target -> automatic lambda conversion
- Kotlin needs `fun interface` to opt in
- function type != fun interface (wrap to convert)
- fun interface = exactly one abstract method
basics
~10 sJava interfaces with one method (like Runnable) can accept a lambda through SAM conversion. Kotlin's own function types are different objects, but you can mark a Kotlin interface 'fun interface' to allow lambdas too.
solid answer
~50 sKotlin's native function values are `FunctionN` objects, distinct from Java single-abstract-method (SAM) interfaces like `Runnable` or `Comparator`. When you call a Java method expecting a SAM interface, Kotlin performs **SAM conversion**: a lambda is wrapped into an instance of that interface automatically — `executor.submit { work() }`. SAM conversion historically applied only to Java interfaces, not Kotlin ones, because Kotlin prefers explicit function types. To opt a Kotlin interface into the same treatment, declare it `fun interface Predicate { fun test(x: Int): Boolean }`; then a lambda can be passed where a `Predicate` is expected. A plain Kotlin function type and a `fun interface` are not assignment-compatible without conversion — you wrap with the interface name, e.g. `Predicate { it > 0 }`. This matters for Java interop and for designing typed callback abstractions with named methods.
code
kotlin · 10 linesfun interface Transformer<T, R> {
fun apply(value: T): R
}
fun <T, R> runAll(items: List<T>, t: Transformer<T, R>): List<R> =
items.map { t.apply(it) }
val upper = Transformer<String, String> { it.uppercase() } // SAM conversion
println(runAll(listOf("a", "b"), upper)) // [A, B]
println(runAll(listOf(1, 2)) { it * 10 }) // lambda literal also converts -> [10, 20]go deeper
Knows you can pass a lambda to Java APIs like Runnable.
Explains SAM conversion for Java targets and basic fun interface usage.
Articulates that function types and fun interfaces are distinct, the conversion rules, single-abstract-method constraint, and interop/design tradeoffs.
Chooses between function-type and fun-interface API surfaces deliberately, weighing interop, overload ambiguity, allocation, and self-documentation.
## Two different things - A **Kotlin function type** `(Int) -> Boolean` is backed by a `FunctionN` object (`Function1`). - A **SAM interface** (Single Abstract Method) is an interface with exactly one abstract method, e.g. Java's `Runnable`, `Callable`, `Comparator`, or a custom one. They are *not* the same type, even if their shapes match. ## SAM conversion for Java interop When a **Java** method expects a SAM interface, Kotlin lets you pass a lambda and **converts** it for you: ```kotlin val pool = Executors.newSingleThreadExecutor() pool.submit { doWork() } // lambda -> Runnable val cmp = Comparator<String> { a, b -> a.length - b.length } // lambda -> Comparator ``` The compiler synthesizes an anonymous `Runnable`/`Comparator` whose single method runs the lambda body. This is **SAM conversion**, and for Java targets it's automatic. ## Kotlin interfaces: `fun interface` By default Kotlin does **not** SAM-convert lambdas into Kotlin interfaces — it wants you to use function types. To opt in, declare a **functional interface** with the `fun` modifier: ```kotlin fun interface IntPredicate { fun accept(i: Int): Boolean } val positive = IntPredicate { it > 0 } // SAM conversion into a Kotlin fun interface println(positive.accept(5)) // true ``` Rules for a `fun interface`: **exactly one abstract method** (it may have other default/non-abstract members). ## Function type vs fun interface — not interchangeable ```kotlin fun useType(p: (Int) -> Boolean) = p(1) fun useSam(p: IntPredicate) = p.accept(1) val lambda = { i: Int -> i > 0 } useType(lambda) // ok // useSam(lambda) // does NOT compile directly useSam(IntPredicate(lambda)) // wrap explicitly, or pass a lambda literal: useSam { it > 0 } ``` A bare function-type value isn't auto-converted into a `fun interface`; you wrap it (or pass a lambda literal, which the compiler can convert). ## When to choose which - **Function type** `(A) -> B`: idiomatic Kotlin, composable, works with stdlib HOFs. - **fun interface**: when you want a **named method** (self-documenting callbacks), Java-interop symmetry, or to add default methods. ## Gotchas - A `fun interface` must have a single abstract method; adding a second breaks SAM conversion. - SAM-converted instances are real objects (allocations), not erased like inlined lambdas. - Overload resolution can get ambiguous when both a function-type and a SAM overload exist.
- Why doesn't Kotlin SAM-convert lambdas into ordinary (non-fun) Kotlin interfaces automatically?Kotlin prefers explicit function types for behavior, so SAM conversion is opt-in via the `fun interface` modifier to keep intent clear and avoid ambiguity.
- What constraint must a fun interface satisfy?It must have exactly one abstract method (additional default/non-abstract members are allowed).
A function type is a universal plug; a SAM interface is a branded socket. SAM conversion is the adapter that lets your plug fit the branded socket.
saying these in an interview costs you the question
- Saying a Kotlin function type and a Java SAM interface are the same type
- Believing lambdas auto-convert into any Kotlin interface (only fun interface)
- Forgetting a fun interface needs exactly one abstract method
- Assuming SAM-converted instances are allocation-free like inlined lambdas
- Passing a stored function-type value to a fun interface param without wrapping