In Kotlin,the code in an object expression can access variables from the scope that contains it, just like the following code:
fun countClicks(window: JComponent) { var clickCount = 0 var enterCount = 0 window.addMouseListener(object : MouseAdapter() { override fun mouseClicked(e: MouseEvent) { clickCount++ } override fun mouseEntered(e: MouseEvent) { enterCount++ } }) }
But why? In Java, it's not allowed to do this, because the life cycle of the object is different from the local variables, so the enterCount
or clickCount
maybe be invalid when you try to access the object. Can someone tell me how Kotlin does this?
Scope functions The Kotlin standard library contains several functions whose sole purpose is to execute a block of code within the context of an object. When you call such a function on an object with a lambda expression provided, it forms a temporary scope. In this scope, you can access the object without its name.
In Kotlin, apply is an extension function on a particular type and sets its scope to object on which apply is invoked. Apply runs on the object reference into the expression and also returns the object reference on completion.
'apply' and 'also' functions return the context object itself. In this case, we don't need to specify the return value. The context object is automatically returned. Check the below table for the comparison.
In certain computer programming languages, the Elvis operator ?: is a binary operator that returns its first operand if that operand is true , and otherwise evaluates and returns its second operand.
In Java, you can only capture (effectively) final variables in anonymous classes and lambdas. In Kotlin, you can capture any variable, even if they are mutable.
This is done by wrapping any captured variables in instances of simple wrapper classes. These wrappers just have a single field that contains the captured variables. Since the instances of the wrappers can be final
, they can be captured as usual.
So when you do this:
var counter = 0 { counter++ }() // definition and immediate invocation, very JavaScript
Something like this happens under the hood:
class Ref<T>(var value: T) // generic wrapper class somewhere in the runtime val counter = Ref(0); // wraps an Int of value 0 { counter.value++ }() // captures counter and increments its stored value
The actual implementation of the wrapper class is written in Java, and looks like this:
public static final class ObjectRef<T> implements Serializable { public T element; @Override public String toString() { return String.valueOf(element); } }
There are also additional wrappers called ByteRef
, ShortRef
, etc. that wrap the various primitives so that they don't have to be boxed in order to be captured. You can find all the wrapper classes in this file.
Credits go to the Kotlin in Action book which contains the basics of this information, and the example used here.
In Kotlin, unlike Java, lambda expressions or anonymous function (as well as local functions and object expressions) can access and modify their closure - variables declared in outer scope. This behavior is as-designed.
Higher order functions and lambdas - Closures
Why Java does not allow this and Kotlin does - capturing closures introduces additional run-time overhead. Java uses simple and fast approach at cost of functionality. Kotlin on the other hand gives you more features - functionality, but it also generates more code behind the scenes to support it.
At the end it is about writing less code to achieve something. If you want to translate above code to Java, it would be more complex.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With