首先,也许你在it这个关键字上停留了好几秒,然后依旧不明其意。其实它也是Kotlin简化Lambda表达的一种语法糖,叫作 单个参数的隐式名称,代表了这个Lambda所接收的单个参数。这里的调用等价于:
listof(1,2,3).forEach{item -> foo(item)}
默认情况下,我们可以直接用it来代表item,而不需要item->进行声明。
其实,这行代码可能出乎你的预料,执行后你会发现什么也没有。为什么这样?这一次,我们必须要借助IDE的帮助了,以下是把foot函数用IDE转化后的Java代码
@JvmStatic
@Nullable
public static final Function0 foo(final int var0){
return (Function0)(new Function0(){
@Override
public Object invoke() {
this.invoke();
return Unit.INSTANCE;
}
public final void invoke(){
int var1 =var0;
System.out.print(var1);
}
});
}
以上是字节码反编译的Java代码,从我们可以发现Kotlin实现Lambda表达式的机理。
2.Function类型
Kotlin在Jvm层设计了Function类型(Function0,Function1.......Function22)来兼容Java的Lambda表达式,其中的后缀数字代表了Lambda参数的数量,如以上的foo函数构建的其中是一个无参Lanbda,所以对应的接口是Function0,如果有一个参数那么对应的就是Function1。 它的源码中是如下定义的:
package kotlin.jvm.functions
interface Function1<in P1,out R>:Kotlin.Function<R>{
fun invoke(p1:P1):R
}
可见每个Function类型都有一个invoke方法,稍后会详细介绍这个方法。
设计Function类型的主要目的之一就是要兼容Java,实现在Kotlin中也能调用Java的Lambda。在Java中,实际上并不支持把函数作为参数,而是通过函数式接口来实现这一特性。所以如果我们要把Java的Lambda传给Kotlin,那么它们就必须实现Kotlin的接口,在kotlin中我们则不需要跟它们打交道。在第6章我们会介绍如何在Kotlin中调用Java的函数式接口。
----------------
神奇的数字-22
也许你会有一个问题,:为什么这列Function类型最大的是Function22?如果Lambda的参数超过了22个,那该怎么办呢?
虽然22个参数已经够了,然而现实中也许我们真的需要超过22个参数。其实,在Scala的设计中也遵循了22个数字的设计,这似乎已经成为了业界的一种惯例。然而,这22的设计也给Scala开发者带来了不少麻烦。所以,Kotlin在设计的时候便考虑到了这种情况,除了23个常用的Function类型外,还有一个FunctionN。在参数真的超过22个的时候,我们就可以靠它来解决问题。更多的细节可以参考百度
--------------
3.invoke 方法
代码清单2-3中foo函数得返回值类型是Function0.这也意味着,如果我们调用了foo(n),那么实质上仅仅是构造了一个Function0对象。这个对象并不等价于我们要调用得过程本身。通过源码可以发现,需要调用Function0得invoke方法才能执行println方法。所以,我们的疑惑也一下解决了,上述的例子必须如下修改,才能够最终的打印我们想要的结果:
fun foo(int:Int) = {
print(int)
}
>>> listOf(1,2,3).forEach{foot(it).invoke()} //增加课invike调用
123
也许你觉得invke这种语法显得丑陋,不符合Kotlin简洁表达的设计理念。确实如此,所以我们还可以用熟悉的括号调用来代替invoke,如下所示:
>>>listOf(1,2,3).forEach{foo(it)()}
网友评论