前言:被Elon Musk圈粉后,觉得他一定是我一辈子的偶像,他正做着一些那些梦寐以求,人类最浪漫最伟大的事情.
1.接收者的函数字面值
首先更正下我在 Kotlin 进阶教程(一)文末分析apply源码的一个错误:
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
这个内联函数,是一个关于T的扩展函数,其入参block的写法,看上去好像不太好理解;T.()是什么玩意?最初我也把它理解成了T的扩展函数,这个理解是不对的,更正一下.虽然它的样子很像扩展函数,但它并不是;它是一个带有接收者的函数字面值. 接受者的函数字面值,怎么理解呢; eg: 匿名函数语法允许直接指定函数字面值的接收者类型 所以可以如下定义一个匿名函数sum
val sum = fun Int.(another: Int): Int = this + another
this代表调用者本身,那么,我就可以这样调用sum; eg:
val sum = 2.sum(3).sum(4)
println("$sum")
输出9; 显而易见,我们可以让调用者也成为入参的一部分,this表示; 所以apply函数可以轻松实现链式调用;
2.中缀函数
观察如下代码
val map = mapOf<Int, String>(1 to "one", 2 to "two")
可以看到1 to "one" 很方便映射了key-value 点进去看一下源码
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
可以看到这个to函数是一个A的扩展函数,入参是B,所以我猜想 to 函数还可以这样调用 1.to("one")
;
并且有infix修饰,infix即中缀函数的修饰符;
很显然1.to("one"),
和1 to one
比较,后者更加简洁优雅;
对于中缀函数,它只能有一个参数,切有infix修饰;
了解了中缀函数,那么我们可以利用它发挥你的想象力,创造一些十分优雅的API .比如我们计算某个天数前的时间戳可以这么写:
val yesterday = 1 days ago
val theDayBeforeYesterday= 2 days ago
代码看上就想在写英文语句一样;
object ago
infix fun Int.days(ago: ago):Long {
....//计算时间
return time
}
3.内联函数
内联函数用inline修饰 在使用高阶函数时会带来一些运行时的效率损失:每一个函数都是一个对象,并且会得到一个闭包; 在(一)中我提过inline函数编译器会将函数编译成执行的代码块,从而避免了函数频繁的压栈和出栈. 我们可以看到Kotlin的源码中,尤其是标准库,大量使用了内联函数,内联函数会是性能有所提升; 所以在我们开发中,一些工具性函数,推荐liline函数;
lambda表达式中禁止裸用return进行函数返回,但是如果lambda 表达式传给的函数是内联的,该 return也可以内联,所以它是允许的:
fun foo() {
lambda { _: Int, _: Int ->
println("内部已返回")
return //方法内联,所以这里是OK的
println("内部未返回")
}
println("lambda局部返回,后续代码执行")
}
inline fun lambda( o: (x: Int, y: Int) -> Unit) {
o.invoke(3, 4)
}
执行foo(),输出:内部已返回; 可以看到这里的return非局部返回
但是如果传入的lambda是内联的,但是又不允许其非局部控制流,那么需要用crossinline修饰 eg:
fun foo() {
lambda { _: Int, _: Int ->
println("内部已返回")
// return //方法内联,但是crossinlie修饰,所以这里是不允许的,
return@lambda //标签是允许的
println("内部未返回")
}
println("lambda局部返回,后续代码执行")
}
inline fun lambda( crossinline o: (x: Int, y: Int) -> Unit) {
o.invoke(3, 4)
}
执行foo(),输出:
内部已返回
lambda局部返回,后续代码执行
结尾
有理解错误的请指正!!! 重要的事情说三遍: kotlin很好用!
kotlin很好用!
kotlin很好用!