版权归作者所有,转发请注明出处:https://www.jianshu.com/p/14a837e67dd7
Kotlin中的inline关键字 - 内联函数
Kotlin中的inline关键字 - 内联类
前言
Kotlin
是一种在 Java
虚拟机上运行的静态类型编程语言,被称之为 Android
世界的Swift
,在Google
I/O 2017中,Google
宣布 Kotlin
成为 Android
官方开发语言
内联函数
被inline
关键字所修饰的函数为内联函数
内联函数的作用
尝试去修饰普通函数
inline fun getAmount() = 0.1
这种写法编辑器会有警告提示:
Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types
意味着使用inline
只有在修饰一个参数为functional types
的函数时候效果最好,也就是我们所说的高阶函数的一种,kotlin
中的forEach
函数中参数action
就是一个函数类型,所以forEach
函数使用了inline
修饰
@kotlin.internal.HidesMembers
public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {
for (element in this) action(element)
}
inline修饰高阶函数的好处
我们对高阶函数分别使用普通函数声明,和使用内联函数声明
class Test {
//不使用inline 修饰的高阶函数
fun test(f: () -> Unit) {
f()
}
//使用inline 修饰的高阶函数
inline fun testInline(f: () -> Unit) {
f()
}
fun call() {
test {
print("test")
}
testInline {
print("testInline")
}
}
}
转化为Java代码
public final class Test {
public final void test(@NotNull Function0 f) {
Intrinsics.checkParameterIsNotNull(f, "f");
f.invoke();
}
public final void testInline(@NotNull Function0 f) {
int $i$f$testInline = 0;
Intrinsics.checkParameterIsNotNull(f, "f");
f.invoke();
}
public final void call() {
this.test((Function0)null.INSTANCE);
int $i$f$testInline = false;
int var3 = false;
String var4 = "testInline";
boolean var5 = false;
System.out.print(var4);
}
}
在call()
中可以看出,第一行,当调用非内联函数时是直接调用了此函数,并且创建了匿名类Function0用于Lambda函数的调用
this.test((Function0)null.INSTANCE);
在call()
中的其他代码可以看出,内联函数则是复制了函数体过来,而没有创建匿名类,而是直接嵌入了Lambda
函数的实现体
int $i$f$testInline = false;
int var3 = false;
String var4 = "testInline";
boolean var5 = false;
System.out.print(var4);
当高阶函数没有使用Inline
修饰时,调用此函数会直接引用此函数,并且会创建匿名类以实现此函数参数的调用,这有两部分开销,直接调用此函数会创建额外的栈帧以及入栈出栈操作(一个函数的调用就是一个栈帧入栈和出栈的过程),并且匿名类的创建也会消耗性能
使用 Inline 修饰高阶函数是会有性能上的提升
避免内联大型函数
在调用内联函数时,基于上述我们已经知道内联会将Lambda
参数函数体直接进行嵌入,避免了函数的引用以及栈帧和对象创建的开销,但是如果Lambda
所内联的函数体数量太大,嵌入则会造成调用位置的函数体增长,所以需要 避免内联大型函数
noinline关键字使用
内联Lambad
只能在内联函数内部使用,或者作为内联参数进行传递,如果想 将内联Lambad
作为普通函数参数进行传递或者存储在字段中则不能使用内联函数声明,或者在声明为内联函数然后将需要传递的Lambda
参数指定为noinline
inline fun testInline(f: () -> Unit) {
testInlineInner(f) //会提示错误,内联Lambad 不能作为普通函数参数进行传递
}
fun testInlineInner(f: () -> Unit) {
f()
}
如果一个函数中有多个函数参数,并且部分参数时可以直接使用内联方式调用,另一部分则需要进行传递或者赋值,则可以将函数声明为内联函数,内联函数中,不符合内联方式的参数使用noinline
修饰
inline fun testInline(f1: () -> Unit, noinline f: () -> Unit) {
f1()
testInlineInner(f)
}
fun testInlineInner(f: () -> Unit) {
f()
}
内联Lambda中使用return
- 在调用普通高阶函数时,在Lambda中不能直接使用retrun退出lambda表达式,需要进行声明然后退出lambda,inline lambda可以直接return,但是退出的是当前外部函数而不是lambda表达式
fun call() {
testOrdinary {
print("testOrdinary")
return //报错
}
testOrdinary {
print("testOrdinary")
return@testOrdinary //正常 退出 testOrdinary
}
testInline {
print("testInline")
return //正常 退出call()
}
testInline {
print("testInline")
return@testInline //正常 退出 testInline
}
}
crossinline关键字使用
当内联Lambda
不是直接在函数体中调用,而是在嵌套函数或者其他执行环境中调用则需要声明为crossinline
inline fun testInline(crossinline callBack: () -> Unit) {
object : Thread(){
override fun run() {
callBack() //执行在object中,需要将callBack声明为 crossinline
}
}
}
Reified 类型的参数
reified 是Kotlin关于范型的关键字,从而达到泛型不被擦除,如果需要使用reified 去修饰泛型方法中的泛型类型,则需要使用Inline修饰此泛型方法,因为Inline函数可以指定泛型类型不被擦除,因为内联函数编译期会将字节码嵌入到调用它的地方,所以编译器才会知道泛型对应的具体类型,使用reified 和 Inline适用于泛型方法,并且方法体中需要对泛型类型做以判断的情况
inline fun <reified T> Bundle.plus(key: String, value: T) {
when (value) {
is String -> putString(key, value)
is Long -> putLong(key, value)
is Int -> putInt(key, value)
}
}
Inline properties
inline
也可以修饰set
,get
方法也会在调用位置进行嵌入代码以收益性能,并且可以直接修饰属性使两个访问器都标记为inline
class Amount(var amount: Long) {
private val isEmpty: Boolean
inline get() {
return amount <= 0
}
private val isEmptyNoInline: Boolean
get() {
return amount <= 0
}
fun test(){
val amount = Amount(10)
val isEmpty = amount.isEmpty
val isEmptyNoInline = amount.isEmptyNoInline
}
//转化为Java代码
public final void test() {
Amount amount = new Amount(10L);
int $i$f$isEmpty = false;
boolean isEmpty = amount.getAmount() <= 0L; //代码嵌入
boolean isEmptyNoInline = amount.isEmptyNoInline(); //非嵌入
}
}
欢迎关注Mike的简书
Android 知识整理
网友评论