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