Kotlin
的lambda
闭包跟Java
的lambda
闭包完全是两个不同的概念。我们都知道,Java
的lambda
表达式就是单方法接口(也就是只有一个方法的接口)匿名实现的语法糖。而Kotlin
的lambda
闭包真正把它抽象成了一种() -> Unit
类型,这种类型我们可以把它理解成一个类。作用就是写回调方法时替代单方法接口,减少一些代码量。
Java的lambda表达式
上面也说了,Java
的lambda
表达式就是单方法接口匿名实现的语法糖,(语法糖的意思就是这种语法对程序的功能没有影响,让使用起来更加简单,看起来更加简洁),下面看一下一个简单的使用
rightYAxis.setValueFormatter(new IAxisValueFormatter() {
@Override
public String getFormattedValue(float value, AxisBase axis) {
return (int) value + "%";
}
});
//Java 8 way: lambda
rightYAxis.setValueFormatter((v, a) ->
// lambda最后一条语句的执行结果表示这个lambda的返回值
(int) v + "%"
);
Kotlin的lambda闭包写法
lambda
最后一条语句的执行结果表示这个lambda
的返回值。下面看一个在Kotlin
中的最常见的写法,通过lambda
闭包写函数回调。
fun setOnCallBackListener(listener: (String) -> Unit) {
// 这两种方式等效,直接调用listener闭包体
listener("send data")
//listener.invoke("send data")
}
fun main() {
setOnCallBackListener({ str: String ->
print(str)
})
}
当lambda
闭包只有一个参数时,可以用it来替代。当lambda
闭包是函数的最后一个参数时,可以将lambda
闭包体移到()
外。如果函数只有一个参数且这个参数是lambda
闭包,可以省略掉()
,所以上面的代码可以省略如下
fun main() {
setOnCallBackListener{
print(it)
}
}
初学者难理解的两个问题
我刚接触Kotlin
的lambda
表达式的时候遇到两个问题比较困惑。我估计很多人多会是这样。对于下面的setOnKotlinCallBackListener
方法,参数为一个单方法接口,在Kotlin
中调用的时候只支持匿名内部类的方式,不支持传入lambda
表达式。但是在Java
中调用的时候确是支持传lambda
表达式,这是为什么?其实弄懂lambda
闭包在Java
和Kotlin
的区别这个问题比较好理解,因为在Kotlin
中 下面代码中的lambda
闭包跟OnKotlinCallBackListener
接口是完全两种不同的类型,两种之间没有任何关系,它不是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)
}
}
public class JavaLambdaDemo {
public static void main() {
KotlinLambdaKt.setOnKotlinCallBackListener((data, success) -> {
System.out.println(data);
});
}
}
第二个问题,如下代码,在Kotlin
中调用Java
的setJavaCallBackListener
方法,参数为单方法接口,我们看到是支持传入lambda
表达式的。这是为什么呢?其实Kotlin
的lambda
闭包用在Java
方法中变成了Java
的lambda
语法糖,其实主要是为了能和Java
完美兼容
interface OnJavaCallBackListener {
void onFinished(String data, boolean success);
}
public class JavaLambdaDemo {
public static void setJavaCallBackListener(OnJavaCallBackListener listener) {
listener.onFinished("complete", true);
}
}
fun main() {
JavaLambdaDemo.setJavaCallBackListener{ str, bool ->
print(str)
}
}
lambda闭包和Function的关系
Kotlin
文件编译后会编译成class
文件,lambda
闭包编译后会被编译成一个Function
接口对象,其实在Kotlin
中lambda
闭包跟Function
接口是等效的。下面的例子中,用Function1
的接口对象传入参数为(String) -> Unit
的方法也是支持的。
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)
}
}
其实Function1
等效于只有一个参数的lambda
闭包,在Kotlin
中,最多只定义了Function22
,也就是最多支持参数个数为22个的lambda
闭包。
/** 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
}
在Kotlin
中lambda
闭包(String)->Unit
跟Function1<String, Unit>
是等效的,所以Java
中调用Kotlin
的lambdab
闭包,需要通过对于参数的Function
接口对象来实现
public static void main() {
KotlinLambdaKt.setOnCallBackListener(new Function1<String, Unit>() {
@Override
public Unit invoke(String s) {
return null;
}
});
}
typealias重命名
跟Swift
一样,Kotlin
也有typealias
,进行类型的重命名
typealias CallBackListener = (String) -> Unit
fun setOnCallBackListener(listener: CallBackListener) {
listener("send data")
//listener.invoke("send data")
}
fun main() {
setOnCallBackListener {
print(it)
}
}
高阶函数
高阶函数是指函数或者lambda
的参数又是一个函数或者lambda
闭包的函数,上面使用的例子中参数为lambda
闭包的函数就是高阶函数
fun main(args: Array<String>) {
onlyif(true, {
println("日志信息$it")
})
//Lambda作为函数的最后一个参数,可以把Lambda写在括号之外
onlyif(true) {
println("日志信息$it")
}
}
//Unit表示无返回值,类似于Java的Void
fun onlyif(isDebug: Boolean, a: (String) -> Unit) {
if (isDebug) {
a.invoke("adada")
}
}
inline修饰符
lambda
闭包在编译时会被编译成Function
接口对象,因此在高阶函数调用时,会产生很多临时的无用对象。可以使用inline
关键字来修饰高阶函数,在编译时,将函数的代码拷贝到调用的地方,减少不必要对象的创建。
fun main(args: Array<String>) {
for (i in 0..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
}
反编译为java
为
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);
}
}
noinline
如果传递给 inline 函数的 lambda,有 return 语句,那么会导致闭包的调用者也返回。这个时候就需要使用noinline
关键字,声明 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
}
)
}
crossinline
表明 inline
函数的形参中的 lambda
不能有 return
语句,这样可以避免使用inline
时,lambda
中的return
语句影响程序流程
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 // 编译错误: return is not allowed here
}
}