Kotlin入门(六):内联方法
2018-04-05 本文已影响66人
无余
- Kotlin系列
0.【翻译】Using Kotlin for Android Development
1.Kotlin入门(一):变量的声明和使用
2.Kotlin入门(二):方法的定义和使用
3.Kotlin入门(三):if, when, for, while
4.Kotlin入门(四):null安全(“?”,“?:”,“!!”)
5.Kotlin入门(五):类与继承
6.Kotlin入门(六):内联方法
7.Kotlin入门(七):enum,data,sealed,object
使用高阶方法会造成一些强制性的开销:内存分配和调用都是开销。不过,在许多情况下有些开销是可以避免的。
例子:
现在把吃饭分为三个步骤:1.拿起筷子 2.吃 一口3.放下筷子
1.情况一:每吃一口都要放下筷子,即拿、吃、放、拿、吃、放...
操作顺序:
1、2、3; 1、2、3; 1、2、3...
这种情况不是饭菜不合胃口、就是吃撑了。
2.情况二:每吃若干口才放下筷子。
操作顺序:
1、2、2...2、3; 1、2、2...2、3;... 1、2、2...2、3;
明显这才是正确操作,能省不少力气。
在JVM中也有类似的情况,JVM中每个线程都有一个虚拟机栈,每个方法的从调用到完成,对应着入栈、出栈的过程,如果将一方法分为多个方法时,就会有更多的入栈、出栈的开销,使用内联方法能有效减少这部分开销。
-
例一
现在我们要打印一个人,分三部分打印:头、身体和脚,其中身体构造复杂,又要分三部分
1-1
fun main(args: Array<String>) {
//printHead
println("head")
//printbody
println("body1")
println("body2")
println("body3")
//printFoot
println("foot")
}
现在我们想把打印身体这部放到一个单独的方法中,使代码更清晰:
1-2
fun main(args: Array<String>) {
//printHead
println("head")
//printbody
printBody()
//printFoot
println("foot")
}
fun printBody() {
println("body1")
println("body2")
println("body3")
}
不过这样一来多了部分开销,这时内联方法可以帮我们避免这部分开销。
-
inline
定义内联方法需使用“inline”关键字修饰
1-3
inline fun printPerson(printBody: () -> Unit) {
//printHead
println("head")
//printbody
printBody()
//printFoot
println("foot")
}
fun main(args: Array<String>) {
printPerson {
println("body1")
println("body2")
println("body3")
}
}
编译过程会帮我们把1-3转换为1-1的样子,提高了性能的同事又能保证结构清晰。
-
例二
这是官方的例子,刚学习的时候,因为没用过Lock,一时间没反应过来,还以为是Kotlin专有的,没怎么看懂。于是搜索了下其它资料,发现基本都是照着抄一遍,还得靠自己。
如何才能将代码
lock(l) { foo() }
实现这样的效果:
l.lock()
try {
foo()
}
finally {
l.unlock()
}
可以通过内联方法实现:
inline fun <T> check(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
} finally {
lock.unlock()
}
}
调用:
var lock = ReentrantLock()
check(lock) {print("hello")}
完整样例:
开启两个线程,通过加同一把锁实现同步:
fun main(args: Array<String>) {
var lock = ReentrantLock()
Thread() {
check(lock) {
for (i in 1..5) {
TimeUnit.SECONDS.sleep(1)
println("111")
}
}
}.start()
Thread() {
check(lock) {
for (i in 1..5) {
TimeUnit.SECONDS.sleep(1)
println("222")
}
}
}.start()
}
inline fun <T> check(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
} finally {
lock.unlock()
}
}
输出:
111
111
111
111
111
222
222
222
222
222
-
noinline
inline 方法中的方法参数默认是inline类型的,如果想过滤掉inline特性,可用noinline修饰。
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {
// ...
}
-
lambda与return
1.普通方法的lambda模块中禁止使用return
fun foo() {
ordinaryFunction {
return // ERROR: can not make `foo` return here
}
}
2.inline方法的lambda模块可用使用return
fun foo() {
inlineFunction {
return // OK: the lambda is inlined
}
}
fun hasZeros(ints: List<Int>): Boolean {
ints.forEach {
if (it == 0) return true // returns from hasZeros
}
return false
}
这么设计很好理解,普通方法的lambda模块其实是另一个方法的方法体,你在本方法中调用另一方法中的return算是怎么回事!反而容易误导自己,干脆禁止了事。
而inline方法可认为是本方法的一部分,外面那层大括号包装可当它不存在,使用return就显得和自然了。
3.那么想要在普通方法lambda return怎么办?只需在return后加“@方法名”即可:
fun f() {
test {
return@test
}
}
fun test(action: () -> Unit) {}
4.inline 类型的方法参数不能直接用于赋值,要想用于赋值得用crossinline 修饰
inline fun f(crossinline body: () -> Unit) {
val f = object: Runnable {
override fun run() = body()
}
}
-
reified
泛型只在编译时起作用,在运行时已经被擦除,所以泛型标记是没法当做对象使用的。
不过在Kotlin中,reified可修饰inline方法中的泛型,对其进行反射操作。
inline fun <reified T> membersOf() = T::class.members
fun main(s: Array<String>) {
println(membersOf<User>())
}
class User() {
var name = "mao"
}