1 说起内联函数,就是在方法名前加个inline关键字。在Kotlin开发随处可见,例如在Kotlin标准函数中。
kotlin
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
/**
* Calls the specified function [block] with the given [receiver] as its receiver and returns its result.
*
* For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#with).
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
/**
* Calls the specified function [block] with `this` value as its receiver and returns `this` value.
*
* For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#apply).
*/
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
/**
* Calls the specified function [block] with `this` value as its argument and returns `this` value.
*
* For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#also).
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
在说内联函数之前,先说其他的概念就是Kotlin的高阶函数其实是实现FunctionN接口的一个实例。Kotlin的lambda作为实参传给参数时,也会创建匿名内部类以及调用invoke方法。下面以简单代码表述一下。
kotlin
//在Kt文件中定义一个高阶函数
private fun lambdaFun(action: () -> Unit) {
println("[lambdaFun]: 执行前")
action()
println("[lambdaFun]: 执行后")
}
private fun testLambdaFun() {
lambdaFun {
println("[testLambdaFun]: 正在执行")
}
}
下面是这段代码的字节码
arduino
private final void lambdaFun(Function0 action) {
String var2 = "[lambdaFun]: 执行前";
System.out.println(var2);
action.invoke();
var2 = "[lambdaFun]: 执行后";
System.out.println(var2);
}
private final void testLambdaFun() {
this.lambdaFun((Function0)null.INSTANCE);
}
这里发现了吧lambda转换成匿名内部类,如果此时频繁多次调用的话,就会导致内存增加,这也就是inline关键字出现的原因。
但当我们把lambdaFun函数加上inline修饰时
ini
private final void lambdaFun(Function0 action) {
int $i$f$lambdaFun = false;
String var3 = "[lambdaFun]: 执行前";
System.out.println(var3);
action.invoke();
var3 = "[lambdaFun]: 执行后";
System.out.println(var3);
}
private final void testLambdaFun() {
int $i$f$lambdaFun = false;
String var3 = "[lambdaFun]: 执行前";
System.out.println(var3);
int var4 = false;
String var5 = "[testLambdaFun]: 正在执行";
System.out.println(var5);
var3 = "[lambdaFun]: 执行后";
System.out.println(var3);
}
可以看到当函数是内联函数时,函数内部的函数体以及函数类型参数都会被内联到调用地方(也就是把函数体的代码和函数类型参数给赋值铺平到调用地方)。它解决了使用lambad方便的同时创建过多的匿名内部类,减小内存的使用。
noline 这个关键字只能修饰函数的参数,而inline是用来修饰函数的,也就是函数是内联的,但是这个函数类型参数不是内联的。
kotlin
/**
* 定义一个高阶函数,非内联
*/
private fun lambdaFun(action: () -> Unit) {
println("[lambdaFun]: 执行前")
action()
println("[lambdaFun]: 执行后")
}
/**
* 测试函数
*/
private inline fun testLambdaFun(action: () -> Unit) {
println("[testLambdaFun]: 执行前")
action()
lambdaFun(action)
println("[testLambdaFun]: 执行后")
}

在内联函数testLambdaFun中调用lambdaFun高阶函数,想把action传给lambadFun,但是在被inline修饰的函数,其参数也会被铺平,也就不会再是函数类型了。所以编译报错了。所有这里的action要使用noinline来修饰。
下面我们说下return问题。

在测试函数中调用高阶函数lambdaFun,看到这里使用return语句,提醒 return@lambdaFun,如果我只使用return的话,那么就需要把lambdaFun定义成内联函数。
kotlin
/**
* 定义一个高阶函数,非内联
*/
private inline fun lambdaFun(action: () -> Unit) {
println("[lambdaFun]: 执行前")
action()
println("[lambdaFun]: 执行后")
}
/**
* 测试函数
*/
private fun testLambdaFun() {
lambdaFun {
println("[testLambdaFun]: 调用中")
return 1处
}
}
1处这里居然直接不报错了,由于内联函数会把函数体和lambad给复制铺平到调用地方,所以这里的return必然是testLambad函数了。
Kotlin直接规定,为了解决lambad表达式中的return语句问题,在非inline函数中,return无法使用(必须return@xx显式指明返回的函数),只有在inline函数中可以使用return,根据inline的特性,这个return必然是返回调用者的函数。
下面我们讲crossinline

kotlin
/**
* 定义一个高阶函数,非内联
*/
private fun lambdaFun(action: () -> Unit) {
println("[lambdaFun]: 执行前")
action()
println("[lambdaFun]: 执行后")
}
/**
* 测试函数
*/
private inline fun testLambdaFun(action: () -> Unit) {
lambdaFun { 2处
action()
}
}
2处这里我们使用lambda表达式,在表达式内调用action()也就是其invoke函数,而不是把action这个函数类型参数进行传递,
可以看到我们其实有两种方式调用lambda高阶函数的。
返回到上处地方报错。 
报错信息意思:无法内联action参数,原因是它可能包含return语句。
原因看起来也简单,在lambda表达式中,只有在内联函数中才能使用return。
kotlin
/**
* 测试函数
*/ 3处
private inline fun testLambdaFun(action: () -> Unit) {
lambdaFun { 4处
action()
}
}
3处和4处满足了是inline函数和在表达式中,而且无法确定action中会不会包含return语句。inline受限严重,这里只有突破这个限制才可以,所以这里采用的方式就是把action加个crossinline修饰符。
crossinle的作用仅仅是当被这个修饰符的参数会告诉IDE来检查你的代码中没有return。