skip to content

Name situations where SAM conversion does NOT apply, and explain how a Kotlin function type passed to Java differs from a SAM-converted lambda.

level: seniorimportance: should knowfreq 30%

answer

  1. Interface only (abstract class excluded)
  2. Exactly one abstract method
  3. Java or fun interface, not plain Kotlin interface
  4. Function types compile to FunctionN.invoke
  5. fun interface = Java-friendly callbacks

basics

~20 s

SAM conversion needs a Java interface with exactly one abstract method as the target. It won't fire for abstract classes, multi-method interfaces, plain Kotlin interfaces, or when the parameter is already a Kotlin function type. A function type passed to Java surfaces as a FunctionN object, not your interface.

solid answer

~50 s

SAM conversion requires: (1) the target is an interface, not a class — even an abstract class with one method is excluded; (2) it has exactly one abstract method (default/static methods are fine); (3) for Kotlin interfaces, the fun interface modifier; and (4) you're actually passing a function value at that position. It also doesn't apply when the parameter's declared type is already a Kotlin function type ((T) -> R) — there's nothing to convert. If a Kotlin API takes a function type and you call it from Java, Kotlin exposes it as kotlin.jvm.functions.FunctionN (e.g. Function1) with an invoke method, so Java code must implement/instantiate that, not a friendly SAM interface. That's why libraries intended for Java consumers often declare a fun interface or a Java SAM type instead of a raw Kotlin function type.

code

kotlin · 8 lines
kotlin
// Java-consumer-friendly API uses a fun interface:
fun interface Transformer { fun transform(s: String): String }
fun process(t: Transformer): String = t.transform("in")
// From Java: process(s -> s.toUpperCase());  // clean single-method lambda

// Raw function type leaks Function1 to Java:
fun processFn(t: (String) -> String): String = t("in")
// From Java the param type is kotlin.jvm.functions.Function1<String,String>

go deeper

for a junior

Can state SAM needs a single-method Java interface and a lambda.

for a middle

Lists the disqualifying cases (abstract class, multi-method, plain Kotlin interface).

for a senior

Explains FunctionN compilation and the Java-consumer ergonomics difference between function types and fun interfaces.

for a principal

Sets API conventions for cross-language libraries, weighing function types vs fun interfaces and the Unit.INSTANCE friction.

## Conditions required for SAM conversion All must hold: 1. **Target is an interface** — not a class. An abstract class with exactly one abstract method does **not** qualify. 2. **Exactly one abstract method.** Multiple abstract methods disqualify it. `default` and `static` methods (Java) don't count. 3. **Java interface, or a Kotlin `fun interface`.** A plain Kotlin interface is rejected. 4. **A function value is being supplied** (lambda or method reference) at that position. ## Cases where it does NOT apply ```kotlin // (a) Abstract class, not interface -> no SAM abstract class Task { abstract fun run() } // val t: Task = { } // ERROR // (b) Two abstract methods -> no SAM interface TwoOps { fun a(); fun b() } // (c) Plain Kotlin interface, no 'fun' -> no SAM interface Plain { fun go() } // (d) Parameter already a Kotlin function type -> nothing to convert fun apply(f: (Int) -> Int) = f(1) apply { it + 1 } // not SAM; this is a function type directly ``` ## Function type vs SAM interface across the JVM boundary A Kotlin function type `(T) -> R` is **not** a special language construct at the bytecode level — it compiles to one of the synthetic interfaces `kotlin.jvm.functions.FunctionN` (here `Function1<T, R>`) with a single `invoke` method. From **Kotlin**, you just write a lambda. But the difference shows when **Java** consumes the API: - If a Kotlin function declares a parameter of type `(Int) -> Int`, Java callers see a parameter of type `kotlin.jvm.functions.Function1<Integer, Integer>` and must implement its `invoke` method or build it via Java's own lambda machinery — and they return `Unit.INSTANCE` for `Unit`-returning ones. Awkward. - If instead the Kotlin API declares a **`fun interface`** or accepts a **Java SAM type**, the Java caller gets a clean single-method interface and can pass a Java lambda naturally. ```kotlin // Java-friendly: fun interface IntOp { fun apply(i: Int): Int } fun run(op: IntOp) = op.apply(10) // Java: run(i -> i + 1); // clean // Java-hostile-ish: fun runFn(op: (Int) -> Int) = op(10) // Java: runFn(i -> i + 1) works in modern Kotlin/Java but exposes Function1 ``` ## Practical guidance - For **Kotlin-only** APIs, prefer function types — they're idiomatic and need no conversion. - For **APIs meant to be called from Java**, prefer a `fun interface` (or a Java SAM type) so Java callers get clean lambdas and a documented method name. - Remember the `Unit` return quirk: function types returning `Unit` force Java callers to `return Unit.INSTANCE`. ## Summary SAM conversion is narrow: interface, single abstract method, Java-or-`fun`, supplying a function value. Outside those, you write an object expression, change the target type, or design the API with a `fun interface` for cross-language ergonomics.

  • Does SAM conversion work for an abstract class with a single abstract method?
    No. SAM conversion is restricted to interfaces. An abstract class, even with one abstract method, requires an explicit object expression or subclass.
  • How does a Java caller see a Kotlin parameter of type (Int) -> Unit?
    As kotlin.jvm.functions.Function1<Integer, Unit>; the lambda body must return Unit.INSTANCE, which is why fun interface is preferred for Java-facing APIs.

saying these in an interview costs you the question

  • Thinking SAM conversion applies to abstract classes
  • Believing a multi-abstract-method interface qualifies
  • Not knowing function types compile to FunctionN interfaces
  • Designing Java-facing Kotlin APIs with raw function types unaware of the Function1/Unit.INSTANCE friction

context