阅读完需:约 6 分钟
内联函数
内联函数使用关键字内联声明,内联函数的使用增强了高阶函数的性能。 内联函数告诉编译器将参数和函数复制到调用站点。


forEash 本身就是一个内联函数因为函数前面有inline,为我们创建了一个for循环

这就是内联函数调用for循环而来的内容

inline 修饰符影响函数本身和传给它的 lambda 表达式:所有这些都将内联到调用处。 内联可以理解为嵌入到调用处,避免函数的反复调用
虚函数或局部函数不能声明为内联。 以下是内联函数内部不支持的一些表达式和声明:
- 局部类声明
- 内部嵌套类的声明
- 函数表达式
- 声明局部函数
局部可选参数的默认值
让我们看一下内联函数的基本示例:
fun main(args: Array<String>) {
inlineFunction({ println("调用内联函数")})
}
inline fun inlineFunction(myFun: () -> Unit ) {
myFun()
print("内联函数内的代码")
}
执行上面示例代码,得到以下结果 –
调用内联函数
内联函数内的代码


非局部返回
在 Kotlin 中,我们可以只使用一个正常的、非限定的 return
来退出一个命名或匿名函数。这意味着要退出一个 lambda 表达式,我们必须使用一个标签,并且在 lambda 表达式内部禁止使用裸 return
,因为 lambda 表达式不能使包含它的函数返回:
fun foo() {
ordinaryFunction {
return // 错误:不能使 `foo` 在此处返回
}
}
但是如果 lambda 表达式传给的函数是内联的,该 return 也可以内联,所以它是允许的:
fun foo() {
inlined {
return // OK:该 lambda 表达式是内联的
}
}

这种返回(位于 lambda 表达式中,但退出包含它的函数)称为非局部返回。 我们习惯了在循环中用这种结构,其内联函数通常包含:
fun hasZeros(ints: List<Int>): Boolean {
ints.forEach {
if (it == 0) return true // 从 hasZeros 返回
}
return false
}


return 例子:
fun main(args: Array<String>) {
inlineFunction({ println("调用内联函数")
return},{ println("内联函数中的下一个参数")})
}
inline fun inlineFunction(myFun: () -> Unit, nxtFun: () -> Unit) {
myFun()
nxtFun()
print("内联函数内的代码")
}
执行上面示例代码,得到以下结果 –
调用内联函数
crossline注解
一些内联函数可能调用传给它们的不是直接来自函数体、而是来自另一个执行上下文的 lambda 表达式参数,例如来自局部对象或嵌套函数。在这种情况下,该 lambda 表达式中也不允许非局部控制流。
要防止从lambda表达式和内联函数本身返回,可以将lambda
表达式标记为crossinline
。 如果在lambda表达式中找到了return
语句,则会抛出编译器错误。


inline fun f(crossinline body: () -> Unit) {
val f = object: Runnable {
override fun run() = body()
}
// ……
}
fun main(args: Array<String>) {
inlineFunction({ println("calling inline functions")
return // compile time error
},{ println("next parameter in inline functions")})
}
inline fun inlineFunction(crossline myFun: () -> Unit, nxtFun: () -> Unit) {
myFun()
nxtFun()
print("code inside inline function")
}
禁用内联 noinline
在内联函数中,当想要将内联函数中传递的一些lambda作为内联函数时,使用noinline
修饰符标记其他函数参数。它用于设置不在调用中内联的表达式。

fun main(args: Array<String>) {
inlineFunctionExample({ println("调用内联函数")},
{ println("内联函数中的下一个参数")} )
println("这是关闭main函数")
}
inline fun inlineFunctionExample(myFun: () -> Unit, noinline nxtFun: () -> Unit ) {
myFun()
nxtFun()
println("内联函数内的代码")
}
执行上面示例代码,得到以下结果 –
调用内联函数
内联函数中的下一个参数
内联函数内的代码
这是关闭main函数
如果内联函数不包含noinline
函数参数且没有reified
(具体)类型参数,则编译器将生成警告。
这个是为了满足inline特性而设计的语法糖,因为给函数使用内联之后,编译器会用其函数体来替换掉函数调用,而如果该函数里面有泛型就可能会出现编译器不懂该泛型的问题,所以引入reified,使该泛型被智能替换成对应的类型
具体化的类型参数
有时候我们需要访问一个作为参数传给我们的一个类型:
fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? {
var p = parent
while (p != null && !clazz.isInstance(p)) {
p = p.parent
}
@Suppress("UNCHECKED_CAST")
return p as T?
}
在这里我们向上遍历一棵树并且检查每个节点是不是特定的类型。这都没有问题,但是调用处不是很优雅:
treeNode.findParentOfType(MyTreeNode::class.java)
我们真正想要的只是传一个类型给该函数,即像这样调用它:
treeNode.findParentOfType<MyTreeNode>()
为能够这么做,内联函数支持具体化的类型参数,于是我们可以这样写:
inline fun <reified T> TreeNode.findParentOfType(): T? {
var p = parent
while (p != null && p !is T) {
p = p.parent
}
return p as T?
}
我们使用 reified
修饰符来限定类型参数,现在可以在函数内部访问它了,几乎就像是一个普通的类一样。由于函数是内联的,不需要反射,正常的操作符如 !is
和 as
现在都能用了。此外,我们还可以按照上面提到的方式调用它:myTree.findParentOfType<MyTreeNodeType>()
。虽然在许多情况下可能不需要反射,但我们仍然可以对一个具体化的类型参数使用它:
inline fun <reified T> membersOf() = T::class.members
fun main(s: Array<String>) {
println(membersOf<StringBuilder>().joinToString("\n"))
}
普通的函数(未标记为内联函数的)不能有具体化参数。不具有运行时表示的类型(例如非具体化的类型参数或者类似于Nothing
的虚构类型)不能用作具体化的类型参数的实参。

如果改变了属性的get/set方法使之变成内联,那么属性内部会自动引用调用的那个属性