skip to content

What is a SAM constructor (e.g. `Runnable { ... }`), and when do you need to use it explicitly instead of a bare lambda?

level: middleimportance: should knowfreq 45%

answer

  1. SAM constructor = InterfaceName { lambda }
  2. Use when expected type can't be inferred
  3. Disambiguates overloads (Runnable vs Callable)
  4. Bare val lambda would be () -> Unit, not the interface
  5. Generic: Comparator<String> { ... }

basics

~20 s

A SAM constructor wraps a lambda into a specific interface by writing the interface name before the lambda, like Runnable { ... }. You use it when the target type is ambiguous — for overloads, generics, or storing the value in a variable.

solid answer

~40 s

A SAM constructor is a compiler-synthesized factory that converts a lambda into a named functional interface: Runnable { print("x") } or Comparator<Int> { a, b -> a - b }. With a bare lambda, the compiler infers the target interface from the expected type. But sometimes there's no single expected type to infer from: assigning to a val without an explicit type, calling an overloaded method where multiple overloads accept different SAM interfaces, or returning a lambda from a function. In those cases you name the SAM constructor explicitly to tell the compiler which interface to build. It also disambiguates between two Java overloads, e.g. one taking Runnable and one taking Callable. The result is an instance of that interface, so you can also call its method directly: Runnable { ... }.run().

code

kotlin · 14 lines
kotlin
import java.util.concurrent.Callable
import java.util.concurrent.Executors

val exec = Executors.newSingleThreadExecutor()

// Stored as a real Runnable, not () -> Unit
val cleanup: Runnable = Runnable { println("cleanup") }
exec.execute(cleanup)

// Disambiguate: submit(Runnable) vs submit(Callable<T>)
val future = exec.submit(Callable { 6 * 7 })
println(future.get())  // 42

exec.shutdown()

go deeper

for a junior

Recognizes the Runnable { } / Comparator { } syntax as wrapping a lambda.

for a middle

Knows when inference fails and you must name the SAM constructor (variables, overloads).

for a senior

Explains overload resolution and generic type arguments on the constructor, and that it equals an object expression.

for a principal

Reasons about API surfaces that force explicit SAM constructors and how that affects readability and overload design.

## What a SAM constructor is For every applicable SAM interface, Kotlin generates a synthetic function with the **same name as the interface** that takes a lambda and returns an instance of the interface. This is the **SAM constructor**: ```kotlin val r: Runnable = Runnable { println("hi") } val cmp = Comparator<Int> { a, b -> a - b } ``` It looks like a constructor call but it's really sugar for `object : Runnable { override fun run() = ... }`. ## When a bare lambda is enough If the **expected type** is known, you don't need the constructor — the compiler infers the target: ```kotlin Thread { println("hi") } // expected type Runnable, inferred executor.submit { compute() } // expected type Callable / Runnable ``` ## When you MUST name the SAM constructor 1. **Storing in a variable with no explicit type:** ```kotlin val task = Runnable { cleanup() } // without it: type would be () -> Unit ``` A bare `val task = { cleanup() }` would have type `() -> Unit`, not `Runnable`, so you couldn't pass it where a `Runnable` is required without conversion at the call site. 2. **Overload disambiguation.** If a Java class has both `schedule(Runnable)` and `schedule(Callable<*>)`, a bare lambda may be ambiguous; naming the constructor picks the overload: ```kotlin service.schedule(Callable { 42 }) // unambiguous ``` 3. **Returning a SAM instance from a function** whose return type is the interface but where you want clarity, or where inference fails. 4. **Passing to a generic method** where the expected type isn't a concrete SAM interface and inference can't pin it down. ## You can call the method immediately Because the constructor yields an instance, you can chain the method: ```kotlin Runnable { println("once") }.run() ``` ## Generic SAM constructors take type arguments For generic interfaces you supply the type argument on the constructor: ```kotlin val byLen = Comparator<String> { a, b -> a.length - b.length } ``` ## Works for fun interfaces too The same machinery generates a SAM constructor for Kotlin `fun interface`s, so `IntPredicate { it > 0 }` works identically.

  • What type does `val x = { 42 }` have, and how is that different from `val x = Callable { 42 }`?
    The first is () -> Int (a Kotlin function type). The second is a Callable<Int> instance produced by the SAM constructor.
  • Can you invoke the single method right after constructing it?
    Yes: Runnable { ... }.run() works because the SAM constructor returns an instance of the interface.

saying these in an interview costs you the question

  • Thinking a bare val lambda already has the interface type
  • Not knowing SAM constructors help resolve overload ambiguity
  • Forgetting generic SAM constructors need a type argument
  • Believing the SAM constructor is a real runtime constructor rather than synthetic sugar

context