阅读 320
Kotlin 学习笔记(十) 内联函数

Kotlin 学习笔记(十) 内联函数

前言 本博客为本人学习笔记,如有不对的地方,劳请在评论区指出,望海涵

Kotlin 学习笔记 (九)

参考文章白话Kotlin

1. 内联函数

说起内联函数 我们首先要谈论起 C++

内联函数是C++的增强特性之一,用来降低程序的运行时间。当内联函数收到编译器的指示时,即可发生内联:编译器将使用函数的定义体来替代函数调用语句,这种替代行为发生在编译阶段而非程序运行阶段

1.1 内联含义

  1. 在kotlin中,函数就是对象,当你调用某个函数的时候,就会创建相关的对象。

你看,这就是空间上的开销!

  1. 当你去调用某个函数的时候,虚拟机会去找到你调用函数的位置。然后执行函数,然后再回到你调用的初始位置。

你看,这就是时间上的开销!

现在,我直接把调用的函数里面的代码放到我调用的地方,省去寻找调用函数的位置的时间。

  正常的调用函数的方式开销


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()

那么该怎么写? 内联函数支持 具体的类型参数的声明 reified 这么写的:

inline fun <reified T> TreeNode.findParentOfType(): T? {
    var p = parent
    while (p != null && p !is T) {
        p = p.parent
    }
    return p as T?
}
复制代码
文章分类
Android
文章标签