How setOnClickListener works in Kotlin: under the hood
\n\nEvery 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\nIf you've ever written something like:
\n\nbutton.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\nThe classic Java approach: where it all began
\n\nIn Java, setOnClickListener looked like this:
\n\nbutton.setOnClickListener(new View.OnClickListener() {\n @Override\n public void onClick(View v) {\n // your code\n }\n});\n\n\nHere 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.
Kotlin transformation: how it looks now
\n\nIn Kotlin, the same code can be written in one line:
\n\nbutton.setOnClickListener { \n println("Button clicked")\n}\n\n\nBut what happens under the hood? Let's break it down step by step.
\n\nStep 1. SAM conversion
\n\nSAM (Single Abstract Method) is an interface with one abstract method. View.OnClickListener is exactly that: it has only one method onClick(View).
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.
So when you write:
\n\nbutton.setOnClickListener { v -> println("Clicked $v") }\n\n\n...Kotlin generates bytecode roughly like this:
\n\nbutton.setOnClickListener(new OnClickListener() {\n @Override\n public void onClick(View v) {\n System.out.println("Clicked " + v);\n }\n});\n\n\nStep 2. Parameter shorthand
\n\nIf the lambda takes one parameter, you can omit it and use the implicit name it:
button.setOnClickListener { \n println("Clicked $it")\n}\n\n\nThis works because Kotlin knows: the only parameter of the onClick method is View. The compiler automatically substitutes it.
What happens at the bytecode level?
\n\nLet'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\nConsider 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
Example with capturing:
\n\nvar counter = 0\nbutton.setOnClickListener {\n counter++\n println("Clicks: $counter")\n}\n\n\nHere Kotlin creates an object that stores a reference to counter. This is slightly more expensive, but in most cases it's imperceptible.
Alternatives to setOnClickListener in Kotlin
\n\nAlthough lambdas are the most popular way, there are other approaches. Let's look at them.
\n\n1. Method reference
\n\nIf you already have a function that matches the signature, you can pass a reference to it:
\n\nfun handleClick(view: View) {\n println("Handler: $view")\n}\n\nbutton.setOnClickListener(::han