Question

Create Anonymous Interface Instances in Kotlin with SAM Conversion

kotlinjavalambdaanonymous

Question

A third-party Java library defines the following generic interface:

public interface Handler<C> {
    void call(C context) throws Exception;
}

In Java, it can be implemented with an anonymous class:

Handler<MyContext> handler = new Handler<MyContext>() {
    @Override
    public void call(MyContext context) throws Exception {
        System.out.println("Hello world");
    }
};

handler.call(myContext); // Prints "Hello world"

How can this interface be implemented concisely in Kotlin, and how can the handler be called?

Short Answer

You will learn how Kotlin implements Java interfaces without creating a named class. For a Java interface with exactly one abstract method, Kotlin can use SAM conversion and a lambda. You will also learn the longer object : Interface form for cases where a lambda is not appropriate.

Concept

A Java interface that has exactly one abstract method is often called a SAM interface: Single Abstract Method. Handler<C> qualifies because its only abstract method is call(C).

Kotlin interoperates directly with Java. When Kotlin expects a Java SAM interface, it can convert a lambda into an object that implements that interface. This is called SAM conversion.

val handler = Handler<MyContext> { context ->
    println("Hello world")
}

The lambda body becomes the implementation of Handler.call.

This matters because Java libraries commonly accept callback interfaces for events, background work, HTTP responses, database transactions, and error handling. SAM conversion lets Kotlin use those APIs without Java's verbose anonymous-class syntax.

The Java declaration throws Exception does not force Kotlin callers or implementations to declare or catch the exception. Kotlin has no checked exceptions. You may still throw or catch exceptions when appropriate.

Mental Model

Think of an interface as a job description. Handler<MyContext> says: “Provide someone who can perform one job: call with a MyContext.”

In Java, you fill out a full form—the anonymous class—to hire that worker. In Kotlin, when there is only one job, a lambda is enough:

Handler<MyContext> { context ->
    println(context)
}

Kotlin knows the lambda is the answer to the interface's one required method. If the interface has several required jobs, Kotlin needs the fuller object form so you can define each one.

Take Quiz

Syntax and Examples

For this Java interface:

public interface Handler<C> {
    void call(C context) throws Exception;
}

use a Kotlin lambda through SAM conversion:

val handler: Handler<MyContext> = Handler { context ->
    println("Hello world")
}

handler.call(myContext)

context is inferred as MyContext from Handler<MyContext>. The explicit type on handler is optional, but can make code easier to read:

val handler = Handler<MyContext> { context: MyContext ->
    println("Handling: $context")
}

If the parameter is not needed, use _:

val handler = Handler<MyContext> { _ ->
    println("Hello world")
}

You can also use an object expression, Kotlin's equivalent of a Java anonymous class:

val handler = object : Handler<MyContext> {
    override fun call(context: MyContext) {
        println("Hello world")
    }
}

Use the lambda form for a single-method interface. Use the object form when you need to implement multiple abstract methods, override additional methods, or maintain more complex object-specific state.

Step by Step Execution

Consider this code:

val handler = Handler<MyContext> { context ->
    println("Hello, ${context.name}!")
}

val myContext = MyContext("Ada")
handler.call(myContext)

Execution proceeds as follows:

  1. Handler<MyContext> { ... } asks Kotlin to create an implementation of the Java Handler interface.
  2. Because Handler has one abstract method, Kotlin maps the lambda to call(context).
  3. myContext is created with name equal to "Ada".
  4. handler.call(myContext) invokes the generated implementation's call method.
  5. The lambda receives myContext as context.
  6. The output is:
Hello, Ada!

A minimal context class for this example is:

data class MyContext(val name: String)

Real World Use Cases

Java SAM interfaces are frequently used as callbacks in Kotlin applications:

  • UI events: Run code when a button is clicked or a menu item is selected.
  • Library hooks: Supply behavior to a framework that invokes it later.
  • Asynchronous APIs: Handle a completed task, result, or failure.
  • Data processing: Pass one operation that processes each record or message.
  • Configuration: Provide a small piece of custom policy, such as retry behavior or authorization logic.

For example, a library may call a handler for each imported record:

val logHandler = Handler<ImportRecord> { record ->
    println("Imported record: ${record.id}")
}

The library owns when call runs; your lambda provides what should happen.

Take Quiz

Real Codebase Usage

In production code, developers usually keep callback implementations short and name meaningful behavior when it grows.

A lambda is clear for a small handler:

val auditHandler = Handler<RequestContext> { context ->
    auditLog.write("Request received: ${context.requestId}")
}

Use a guard clause to ignore input that does not need processing:

val activeUserHandler = Handler<UserContext> { context ->
    if (!context.isActive) return@Handler

    sendWelcomeMessage(context.user)
}

return@Handler returns from the lambda passed to the Handler constructor, rather than trying to return from an outer function.

When logic becomes substantial, move it to a named function and pass a function reference when the signature matches:

fun logContext(context: MyContext) {
    println(context)
}

val handler = Handler<MyContext>(::logContext)

Use an object expression when the implementation needs several overridden members or a dedicated object with internal state:

val countingHandler = object : Handler<MyContext> {
    private var count = 0

    override fun call(context: MyContext) {
        count++
        println("Handled $count contexts")
    }
}

Common Mistakes

Forgetting that the interface must have one abstract method

A lambda only works through SAM conversion when the target Java interface has one abstract method. For an interface with two required methods, use an object expression.

// Does not work if MultiHandler has two abstract methods.
val handler = MultiHandler { println("Only one method is supplied") }
val handler = object : MultiHandler {
    override fun onSuccess() = println("Success")
    override fun onFailure() = println("Failure")
}

Writing Java anonymous-class syntax in Kotlin

This is Java-style syntax and is invalid Kotlin:

// Invalid Kotlin
val handler = new Handler<MyContext>() {
    override fun call(context: MyContext) {}
}

Kotlin uses either Handler<MyContext> { ... } or object : Handler<MyContext> { ... }.

Omitting override in an object expression

Kotlin requires override when implementing an interface method:

// Invalid: missing override
val handler = object : Handler<MyContext> {
    fun call(context: MyContext) {}
}

Correct version:

val handler = object : Handler<MyContext> {
    override fun call(context: MyContext) {}
}

Expecting Java checked-exception rules

You do not add throws Exception to the Kotlin override merely because the Java method declares it. Catch exceptions when you can recover, and allow them to propagate when that is appropriate.

Accidentally using return from the wrong scope

Inside a callback, use a labeled return such as return@Handler to stop only that callback invocation. An unlabeled return is not always allowed and may attempt to return from the enclosing function.

Comparisons

ApproachBest useExample
SAM conversion lambdaA Java interface has one abstract method and the behavior is short.Handler<MyContext> { println(it) }
Object expressionSeveral methods must be implemented, or the implementation needs a custom object body.object : Handler<MyContext> { override fun call(...) {} }
Named classThe implementation is reused, tested independently, or has significant state.class LoggingHandler : Handler<MyContext> { ... }
Function referenceAn existing function already has the required parameter and return types.Handler<MyContext>(::logContext)

A Kotlin fun interface is Kotlin's explicit way to declare a one-method functional interface:

fun interface KotlinHandler<C> {
    fun call(context: C)
}

For the third-party Java Handler, you cannot change its declaration, but Kotlin can still use SAM conversion because it is a Java single-method interface.

Take Quiz

Cheat Sheet

  • A Java interface with one abstract method is a SAM interface.
  • Implement Handler<MyContext> concisely:
val handler = Handler<MyContext> { context ->
    println("Hello world")
}
  • Invoke it normally:
handler.call(myContext)
  • Ignore the argument with _:
val handler = Handler<MyContext> { _ -> println("Hello world") }
  • Use Kotlin's anonymous-object syntax when needed:
val handler = object : Handler<MyContext> {
    override fun call(context: MyContext) {}
}
  • Kotlin does not require Java-style throws Exception declarations.
  • An object expression must use override.
  • For an early exit inside the constructor lambda, use return@Handler.

FAQ

Can Kotlin use a lambda for a Java interface?

Yes. If the Java interface has exactly one abstract method, Kotlin can perform SAM conversion and turn a lambda into an implementation.

How do I implement Handler<MyContext> in Kotlin?

val handler = Handler<MyContext> { context ->
    println("Hello world")
}

Do I need to write throws Exception in Kotlin?

No. Kotlin does not enforce checked exceptions. You can still throw and catch Exception values normally.

What is Kotlin's version of a Java anonymous class?

Use an object expression:

val handler = object : Handler<MyContext> {
    override fun call(context: MyContext) {}
}

Why does the lambda form not work for my interface?

The interface may have more than one abstract method, so it is not a SAM interface. Use an object expression or a named class.

Can I omit the type of context in the lambda?

Usually yes. Kotlin infers it from Handler<MyContext>:

val handler = Handler<MyContext> { context -> println(context) }

When should I use a named class instead of a lambda?

Use a named class when the behavior is large, reused in several places, or needs focused unit tests and substantial state.

Take Quiz

Mini Project

Description

Create a small notification dispatcher that accepts a Java-style Handler<Notification> callback. It demonstrates defining a handler with Kotlin SAM conversion and invoking it with different notification data.

Goal

Implement a handler that prints a formatted message only for high-priority notifications.

Requirements

  1. Create a Notification type with a message and priority.
  2. Create a Handler<Notification> using a Kotlin lambda.
  3. Print a message when the priority is 8 or higher.
  4. Ignore lower-priority notifications.
  5. Call the handler with at least two notifications.
Take Quiz

Keep learning

Related questions

View more