前言
本博客为本人学习笔记,如有不对的地方,劳请在评论区指出,望海涵。本篇为 csdn 原文章 转移修改版 原文章
参考文章白话Kotlin
简述:
- 内联函数的含义
- 内联函数的定义 和调用
- 内联属性
1. 内联函数
说起内联函数 我们首先要谈论起 C++
内联函数是C++的增强特性之一,用来降低程序的运行时间。当内联函数收到编译器的指示时,即可发生内联:编译器将使用函数的定义体来替代函数调用语句,这种替代行为发生在编译阶段而非程序运行阶段
1.1 内联含义
在kotlin中,函数就是对象,当你调用某个函数的时候,就会创建相关的对象。
你看,这就是空间上的开销!当你去调用某个函数的时候,虚拟机会去找到你调用函数的位置。然后执行函数,然后再回到你调用的初始位置。
你看,这就是时间上的开销!ok,现在,我直接把调用的函数里面的代码放到我调用的地方,省去寻找调用函数的位置的时间。
正常的调用函数的方式 及 开销
fun main(args: Array<String>) {
println("你好!")
//函数调用就会去寻找该函数的位置,调用完后在回到这里
// 这个过程就是一种消耗
shopImpl()
fun shopImpl(): Unit {
println("hello world1")
println("hello world2")
}
}
将fun shopImpl()函数标识为内联inline fun shopImpl(),神奇的事情发生了,你的代码会变成如下的模样:
fun main(args: Array<String>) {
println("你好!")
// 我们继续调用 shopImpl函数
shopImpl()
// 但其实运行后代码 不再是 寻找shopImpl函数,
// shopImpl 函数内容直接就在这里 不用寻找调用
// 在编译器 编译器就已经帮我们做好了这部分的替换
println("hello world1")
println("hello world2")
...
inline fun shopImpl(): Unit {
println("hello world1")
println("hello world2")
}
}
我们最后通过 两个图片看下 普通调用方式 和 内联函数调用 编译后的区别
普通调用方式
普通调用方式内联函数调用截图 (inline)
内联函数调用1.2 内联函数的定义 和调用
通过 1.1 我们已经知道 ,内联函数我们只需要在函数的头部添加 inline 关键字 就可以了
问题
在kotlin中,函数就是对象,我们可以将函数作为参数传递给函数。
所以问题来了,若我们的内联函数中的参数有函数呢?那这个函数是不是内联函数呢?
答案
当函数为 内联函数,其 参数如果 也是 函数 或者 lambda 函数,默认参数中的函数 默认也为内联函数
// shopImpl 函数为内联函数
// lambda 函数 printString 默认也为内联函数
inline fun shopImpl(printString:(a:String) -> Unit): Unit {
println("hello world 1")
println("hello world 2")
}
// 当然也可以设置为不是内联函数 在lambda 前添加 noinline 修饰符
inline fun shopImpl(noinline printString:(a:String) -> Unit): Unit {
println("hello world 1")
println("hello world 2")
}
2. 内联函数 和 return
在kotlin中,return 只可以用在有名字的函数,或者匿名函数中,使得该函数执行完毕。而针对lambda表达式,你不能直接使用return
/**
* 我们使用 return + @方法名标签 结束当前lambda函数
*/
fun main(args: Array<String>) {
println("start")
forItem({if(it==1)return@forItem else println(it)})
println("end")
}
fun forItem(item: (i:Int) -> Unit) {
var i = 0
while (i++<5){
item(i)
}
}
运行结果
start // 根据打印结果,我们看到 1 被return 了
2
3
4
5
end
但是,如果内联函数 和 return 结合使用 确实没有问题
// forItem 中可以直接使用 return,不需要标签
fun main(args: Array<String>) {
println("start")
forItem({if(it==1)return else println(it)})
println("end")
}
// 设置为内联函数
//
inline fun forItem(item: (i:Int) -> Unit) {
var i = 0
while (i++<5){
item(i)
}
}
运行结果
start
从打印结果分析,和 高级函数结合 return + label 结果差距很大,其实 理解内联函数就很好理解了,内联函数在编译期间,lambda表达式就是一段代码而已,这时候你在lambda中的return,相当于在你调用的方法内return。所以例子中的 return 会直接结束 main 函数,到时后边的 end 也没有打印。
2.2 内联属性 (V1.1起)
对属性来说,我们会有get,set的方法来操作这个属性。get,set就是个函数,我们可以标识他们为内联函数:
val foo: Foo
inline get() = Foo()
var bar: Bar
get() = ...
inline set(v) { ... }
或者 (getter setter 都为内联)
inline var bar: Bar
get() = ...
set(v) { ... }
2.3 具体化的类型参数
有时候我们需要访问一个作为参数传给我们的一个类型:
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>()
那么该怎么写?
内联函数支持 具体的类型参数的声明 reified
这么写的:
inline fun <reified T> TreeNode.findParentOfType(): T? {
var p = parent
while (p != null && p !is T) {
p = p.parent
}
return p as T?
}
网友评论