Kotlin’s lambda closure is a completely different concept from Java’s lambda closure. As we all know, Java lambda expressions are syntactic sugar for anonymous implementations of single-method interfaces (that is, interfaces that have only one method). Kotlin’s lambda closure really abstracts it into a () -> Unit type, which we can think of as a class. Instead of a single method interface when writing callback methods, it saves some code.

Lambda expressions in Java

As mentioned above, Java lambda expressions are syntactic sugar for the anonymous implementation of a single-method interface. Let’s take a look at a simple use

rightYAxis.setValueFormatter(new IAxisValueFormatter() {
    @Override
    public String getFormattedValue(float value, AxisBase axis) {
        return (int) value + "%"; }}); . / / Java 8 way: lambda rightYAxis setValueFormatter (- > / / lambda (v, a) the last statement execution result indicates that the return value of lambda v + (int)"%"
);
Copy the code

Kotlin’s lambda closure

Lambda The result of the execution of the last statement represents the return value of this lambda. Let’s take a look at one of the most common ways Kotlin writes function callbacks through lambda closures.

fun setOnCallBackListener(Listener: (String) -> Unit) {OnCallBackListener: (String) -> Unit"send data")
    //listener.invoke("send data")
}

fun main() {
    setOnCallBackListener({ str: String ->
        print(str)
    })
}
Copy the code

When lambda closure has only one argument, it can be used instead. When a lambda closure is the last argument to a function, you can move the lambda closure out of (). If the function has only one argument and that argument is a lambda closure, the () can be omitted, so the above code can be omitted as follows

fun main() {
    setOnCallBackListener{ 
        print(it)
    }
}
Copy the code

Two difficult questions for beginners

When I first encountered Kotlin’s lambda expressions, I was confused by two questions. I suspect a lot of people do. SetOnKotlinCallBackListener method, in the following parameters for a single method interface, call in Kotlin when only supports anonymous inner class, does not support to lambda expressions. But when called in Java, it does support passing lambda expressions. Why? The difference between lambda closures in Java and Kotlin is easier to understand, because in Kotlin the lambda closures in the following code are completely different types from the OnKotlinCallBackListener interface, and there is no relationship between the two. It is not syntactic sugar in Java.

fun setOnKotlinCallBackListener(listener: OnJavaCallBackListener) {
    listener.onFinished("".true)
}

fun main() {
    setOnKotlinCallBackListener(object : OnJavaCallBackListener{
        override fun onFinished(data: String, success: Boolean) {
            print(data)}}setOnKotlinCallBackListener{ data, success ->
         print(data)
    }
}
Copy the code
public class JavaLambdaDemo {

    public static void main() { KotlinLambdaKt.setOnKotlinCallBackListener((data, success) -> { System.out.println(data); }); }}Copy the code

The second problem, the following code, calls Java’s setJavaCallBackListener method in Kotlin with a single method interface argument, which we see supports incoming lambda expressions. Why is that? In fact, Kotlin’s lambda closures become Java’s lambda syntax sugar in Java methods, mostly for perfect Compatibility with Java

interface OnJavaCallBackListener {
    void onFinished(String data, boolean success);
}

public class JavaLambdaDemo {

    public static void setJavaCallBackListener(OnJavaCallBackListener listener) {
        listener.onFinished("complete".true); }}Copy the code
fun main() {
    
    JavaLambdaDemo.setJavaCallBackListener{ str, bool -> 
         print(str)
    }
}
Copy the code

Lambda closure and Function

The Kotlin file is compiled into a class file, and the lambda closure is compiled into a Function interface object. In Kotlin, lambda closures are equivalent to the Function interface. In the following example, methods passing in (String) -> Unit as an interface object in Function1 are also supported.

fun setOnCallBackListener(listener: (String) -> Unit) {
    listener("send data")
    //listener.invoke("send data")
}

fun main() {
    setOnCallBackListener(object : Function1<String, Unit> {
        override fun invoke(p1: String) {
            print(p1)
        }
    })

    setOnCallBackListener {
        print(it)
    }
}
Copy the code

In fact, Function1 is equivalent to a lambda closure with only one parameter. In Kotlin, Function22 is defined at most, that is, a lambda closure with a maximum of 22 parameters.

/** A function that takes 21 arguments. */
public interface Function21<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, in P14, in P15, in P16, in P17, in P18, in P19, in P20, in P21, out R> : Function<R> {
    /** Invokes the function with the specified arguments. */
    public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21): R
}
/** A function that takes 22 arguments. */
public interface Function22<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, in P14, in P15, in P16, in P17, in P18, in P19, in P20, in P21, in P22, out R> : Function<R> {
    /** Invokes the function with the specified arguments. */
    public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21, p22: P22): R
}

Copy the code

In Kotlin lambda closure (String)->Unit is equivalent to Function1

, so invoking Kotlin’s lambdab closure in Java requires the Function interface object for parameters
,>

    public static void main() {
         KotlinLambdaKt.setOnCallBackListener(new Function1<String, Unit>() {
             @Override
             public Unit invoke(String s) {
                 returnnull; }}); }Copy the code

Typealias rename

Like Swift, Kotlin has TypeAliases, which rename types

typealias CallBackListener = (String) -> Unit

fun setOnCallBackListener(listener: CallBackListener) {
    listener("send data")
    //listener.invoke("send data")
}

fun main() {
    setOnCallBackListener {
        print(it)
    }
}
Copy the code

Higher-order functions

A higher-order function is one in which a function or lambda argument is a function or a lambda closure. In the example used above, the function whose argument is a lambda closure is a higher-order function

fun main(args: Array<String>) {
    onlyif(true, {
         println("Log message$it"}) //Lambda as the last argument to the function, Lambda can be written out of parentheses onlyif(true) {
         println("Log message$it"Void fun onlyif(isDebug: Boolean, a: (String) -> Unit) {if (isDebug) {
        a.invoke("adada")}}Copy the code

The inline modifier

Lambda closures are compiled as Function interface objects at compile time, resulting in many temporary useless objects during higher-order Function calls. You can use the inline keyword to decorate higher-order functions, which, at compile time, copies the code of the function to where it is called, reducing unnecessary object creation.

fun main(args: Array<String>) {
    for (i in0.. 10) { sum(1, 2) { println("Result is: $it") }
    }
}

inline fun sum(a: Int, b: Int, lambda: (result: Int) -> Unit): Int {
    val r = a + b
    lambda.invoke(r)
    return r
}
Copy the code

Decompilation to Java as

public static final void main(@NotNull String[] args) {
   //...
   int var1 = 0;

  for(byte var2 = 10; var1 <= var2; ++var1) {
     byte a$iv = 1;
     int b$iv = 2;
     int r$iv = a$iv + b$iv;
     String var9 = "Result is: " + r$iv; System.out.println(var9); }}Copy the code

noinline

If a lambda passed to an inline function has a return statement, it causes the closure’s caller to return as well. In this case, the noinline keyword is used to declare that the inline function parameter does not want the inline lambda

  inline fun sum(a: Int, b: Int, lambda: (result: Int) -> Unit, noinline lambda2: (result: Int) -> Unit): Int {
      val r = a + b
      lambda.invoke(r)
      lambda2.invoke(r)
      return r
  }

  fun main(args: Array<String>) {
      sum(1, 2,
              { println("Result is: $it") },
              { println("Invoke lambda2: $it") 
                return})}Copy the code

crossinline

Indicates that lambda in parameters of an inline function cannot have a return statement. This prevents the return statement in lambda from affecting the program flow when inline is used

inline fun sum(a: Int, b: Int, crossinline lambda: (result: Int) -> Unit): Int {
    val r = a + b
    lambda.invoke(r)
    return r
}

fun main(args: Array<String>) {
    sum(1, 2) {
        println("Result is: $it")
        return// Compile error:return is not allowed here
    }
}
Copy the code