How setOnClickListener works in Kotlin: under the hood

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

How setOnClickListener works in Kotlin: under the hood

\n\n

Every Android developer switching from Java to Kotlin eventually asks: "Why does setOnClickListener now look so simple and elegant?" And it's not magic — behind it lies a powerful language mechanism that we'll break down piece by piece today.

\n\n

If you've ever written something like:

\n\n
button.setOnClickListener { \n    toast("Clicked!")\n}\n
\n\n

...then you've already used SAM conversion and lambda expressions. But how exactly does Kotlin turn this concise code into something Android understands? Let's find out.

\n\n

The classic Java approach: where it all began

\n\n

In Java, setOnClickListener looked like this:

\n\n
button.setOnClickListener(new View.OnClickListener() {\n    @Override\n    public void onClick(View v) {\n        // your code\n    }\n});\n
\n\n

Here we created an anonymous inner class implementing the View.OnClickListener interface. It worked, but the code was bulky. Kotlin offered an elegant solution — lambda expressions.

\n\n

Kotlin transformation: how it looks now

\n\n

In Kotlin, the same code can be written in one line:

\n\n
button.setOnClickListener { \n    println("Button clicked")\n}\n
\n\n

But what happens under the hood? Let's break it down step by step.

\n\n

Step 1. SAM conversion

\n\n

SAM (Single Abstract Method) is an interface with one abstract method. View.OnClickListener is exactly that: it has only one method onClick(View).

\n\n

Kotlin automatically converts the lambda into an implementation of this interface. This is SAM conversion. The compiler sees that you're passing a lambda where a View.OnClickListener is expected, and creates an anonymous class for you.

\n\n

So when you write:

\n\n
button.setOnClickListener { v -> println("Clicked $v") }\n
\n\n

...Kotlin generates bytecode roughly like this:

\n\n
button.setOnClickListener(new OnClickListener() {\n    @Override\n    public void onClick(View v) {\n        System.out.println("Clicked " + v);\n    }\n});\n
\n\n

Step 2. Parameter shorthand

\n\n

If the lambda takes one parameter, you can omit it and use the implicit name it:

\n\n
button.setOnClickListener { \n    println("Clicked $it")\n}\n
\n\n

This works because Kotlin knows: the only parameter of the onClick method is View. The compiler automatically substitutes it.

\n\n

What happens at the bytecode level?

\n\n

Let's look even deeper. When you use a lambda, Kotlin doesn't always create a new object. In some cases, it uses inline functions or caching.

\n\n

Consider two scenarios:

\n\n
    \n
  • Lambda without capturing variables — the compiler can create a single static instance and reuse it.
  • \n
  • Lambda with capturing variables — a new object is created on each call, because the state needs to be preserved.
  • \n
\n\n

Example with capturing:

\n\n
var counter = 0\nbutton.setOnClickListener {\n    counter++\n    println("Clicks: $counter")\n}\n
\n\n

Here Kotlin creates an object that stores a reference to counter. This is slightly more expensive, but in most cases it's imperceptible.

\n\n

Alternatives to setOnClickListener in Kotlin

\n\n

Although lambdas are the most popular way, there are other approaches. Let's look at them.

\n\n

1. Method reference

\n\n

If you already have a function that matches the signature, you can pass a reference to it:

\n\n
fun handleClick(view: View) {\n    println("Handler: $view")\n}\n\nbutton.setOnClickListener(::han                

Blogs

Book Recommendations