kotlin inline、noline、crossinline
2020-06-23 本文已影响0人
折剑游侠
使用高阶函数时,每个函数都是一个对象,函数调用时还有入栈出栈的开销。
以lock函数为例
fun <T> lock(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
} finally {
lock.unlock()
}
}
调用处
val l = ReentrantLock()
lock(l) {
}
反编译成JAVA
ReentrantLock l = new ReentrantLock();
KtxKt.lock((Lock)l, (Function0)null.INSTANCE);
lambda编译成了函数对象Function0,这样每次调用都会生成函数对象,频繁创建很不友好。
inline
加入inline关键字
inline fun <T> lock(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
} finally {
lock.unlock()
}
}
inline 修饰符影响函数本⾝和传给它的 lambda 表达式:所有这些都将内联到调⽤处。
上述是官方解释,翻译成人话:inline修饰的函数,其本身和参数lambda表达式的内容会直接替换到调用处。省去了函数调用的开销,和生成函数对象。
调用处
val l = ReentrantLock()
lock(l) {
}
反编译成JAVA
ReentrantLock l = new ReentrantLock();
int $i$f$lock = false;
((Lock)l).lock();
try {
boolean var3 = false;
Unit var6 = Unit.INSTANCE;
} finally {
((Lock)l).unlock();
}
加入inline关键字会产生一个新的问题:内联的 lambda 表达式只能在内联函数内部调⽤或者作为可内联的参数传递。
inline fun <T> lock(lock: Lock, body: () -> T): T {
lock.lock()
try {
return get(body)
} finally {
lock.unlock()
}
}
fun <T> get(body: () -> T): T {
return body()
}
编译器会报错,想要将inline的lambda传递到非inline函数,需加上noinline关键字禁用内联。
noinline
inline fun <T> lock(lock: Lock, noinline body: () -> T): T {
lock.lock()
try {
return get(body)
} finally {
lock.unlock()
}
}
fun <T> get(body: () -> T): T {
return body()
}
非局部返回
调用inline函数
val l = ReentrantLock()
lock(l) {
return
}
return可使包含该lambda的函数正常退出
调用非inline函数
val l = ReentrantLock()
lock(l) {
return@lock
}
需要显示指定标签返回
crossinline
⼀些内联函数可能调⽤传给它们的不是直接来⾃函数体、⽽是来⾃另⼀个执⾏上下⽂的 lambda 表达式参数,例如来⾃局部对象或嵌套函数。在这种情况下,该 lambda 表达式中也不允许⾮局部控制流。为了标识这种情况,该lambda 表达式参数需要⽤ crossinline 修饰符标记。
上述为官方解释,说白了就是不允许直接return返回包含lambda的函数,需显示指定标签返回。
var lastTime: Long = 0L
inline fun View.setSingleClick(crossinline onclick: (v: View?) -> Unit) {
this.setOnClickListener {
val currentTime = System.currentTimeMillis()
if (currentTime - lastTime > 500) {
onclick.invoke(it)
}
lastTime = currentTime
}
}
调用处
btContent.setSingleClick {
return@setSingleClick
}
具体化的类型参数
内联函数⽀持具体化的类型参数
inline fun <reified T> ktxClass() = T::class.java
通过reified关键字修饰泛型,可以直接获取泛型的类型。