• Java example
public interface OnClickListener {
        void onClick(View v);
}
Copy the code
  • Kotlin sample
public interface OnClickListener { view -> ... }
Copy the code

The reason this works is that the OnClickListener interface has only one abstract method. This interface is known as a functional interface, or SAM interface. SAM stands for single abstract methods, and Java apis are littered with functional interfaces like Runnable and Callable and the methods that support them.

Pass lambda as an argument to a Java method

A method that can pass a lambda to any expected functional interface.

  • A Runnable parameter is a Runnable parameter.
void postponeComputation(int delay, Runnable computation)
Copy the code

In Kotlin, you can invoke it and pass it a lambda as an argument. The compiler automatically converts it to an instance of Runnable:

postponeComputation(1000) { println(42)}Copy the code

Note that “an instance of Runnable” refers to “an instance of an anonymous class that implements the Runnable interface.” The compiler will help you create it and use lambda as the body of a single abstract method — in this case, the run method.

The same effect can be achieved by explicitly creating an anonymous object that implements Runnable:

    // Pass object expressions as implementations of functional interfaces
    postpostComputation(1000.object: Runnable {
        override fun run(a) {
            println(42)}})Copy the code

Note:

  • When an object is explicitly declared, a new instance is created for each invocation.
  • The case with lambda is different: if the lambda does not access any variables from the function that customized it, the corresponding anonymous class instance can be reused between multiple calls:
// The entire program creates only one instance of Runnable
postponeComputation(1000) { println(42)}Copy the code

Therefore, a fully equivalent implementation would be an explicit object declaration in this code, which stores Runnable instances in a variable and uses this variable every time it is called:

// compile into global variables; There is only one instance in the program
val runnable = Runnable { println(42)}fun handleComputation(a) {
    PostponeComputation is called with an object each time
    postponeComputation(1000, runnable)
}
Copy the code

If lambda captures variables from the enclosing scope, it is no longer possible to reuse the same instance with each invocation. In this case, the compiler creates a new object with the value of the captured variable on each call.

Lambda will capture the variable "id"
fun handleComputation(id: String) {
    // a new instance of Runnable is created each time handleComputation is called
    postponeComputation(1000) { println(id) }
}
Copy the code

Note: The method discussed here to create an anonymous class for lambda and an instance of that class only works for Java methods that expect a functional interface, but using the Kotlin extension method for collections does not work. If a lambda is passed to a Kotlin function marked inline, no anonymous classes will be created. Most library functions are marked inline.

SAM constructor: explicitly convert lambda to a functional interface

SAM constructors are compiler-generated functions that let you perform an explicit conversion from a lambda to a functional interface instance. It can be used in contexts where the compiler does not automatically apply the transformation.

For example, if a method returns an instance of a functional interface rather than returning a lambda directly, use the SAM constructor to guarantee it.

  • Use the SAM constructor to return the value
/ / define
fun createAllDoneRunnable(a): Runnable {
    return Runnable { println("All done!")}}/ / test
>>> createAllDoneRunnable().run()
All done!
Copy the code

The name of the SAM constructor is the same as the name of the underlying functional interface. The SAM constructor accepts only one host — a lambda used as the single abstract method body of a functional interface — and returns an instance of the class that implements the interface.

Lambda and add/remove listeners

Note that there is no this inside a lambda like an anonymous class object: there is no way to refer to an instance of the anonymous class to which the lambda is converted. From the compiler’s point of view, a lambda is a block of code, not an object, and it cannot be treated as an object reference. The this reference in Lambda refers to the class that surrounds it.