Kotlin-23.内联函数(Inline Functions)

官方文档: http://kotlinlang.org/docs/reference/inline-functions.html

1.内联函数的概念和作用

使用高阶函数(higher-order functions)会导致一些性能的损耗:
    每个函数都是对象,且会捕获闭包closure(即变量会在函数体内被访问),
    函数对象/类会增加内存分配,而且虚拟调用栈也会增加额外内存开销!

可用内联函数(inline function)消除这些额外内存开销,
说白了就是在调用处插入函数体代码,以此减少新建函数栈和对象的内存开销!   
被inline修饰的函数或lambda表达式,在调用时都会被内联(在调用处插入函数体代码)
    inline fun lock<T>(lock: Lock, body: () -> T): T {
        lock.lock()
        try {
            return body()
        }
        finally {
            lock.unlock()
        }        
    }

    print("开始************")
    lock(l) { foo() }
    print("结束************")

    //编译器实际生成以下代码(就是直接把代码插入到调用处):     
    print("开始************")
    l.lock()
    try {
        foo()
    }
    finally {
        l.unlock()
    }
    print("结束************")

很明显,内联可能导致编译器生成的代码增加,但如果使用得当(不内联大函数),在性能上有很大提升,
尤其是在循环的megamorphic处调用!

禁用内联(noinline)
如果内联函数的有些(作为参数)lambda表达式不是内联,可用noinline修饰符函数参数!
    inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {
        // ……
    }

2.非局部返回(Non-local returns)

在内联的lambda表达式中退出包含它的函数称为非局部返回(Non-local returns)!
在Kotlin中,正常return(没有限定符@)表示退出一个命名或匿名函数
所以要退出lambda表达式,return需要加限定符@标签,
在非内联的lambda表达式中禁用正常return(没有限定符@):
    fun f1(p: ()->Unit) {
        p()
    }

    inline fun f2(p: ()->Unit){
        p()
    }

    fun main(args: Array<String>) {
        f1{
            //f2和lambda表达式都不是内联
            println("Hello, world!")
            return //编译错误,此处不允许return
        }
        
        f2{
            //f2是内联函数,lambda表达式也是内联
            println("Hello, world!")
            return //编译正确,可以return
        }
        
        listOf(1,2,3).forEach {
            //forEach是内联
            if(it==2) return //编译正确,可以return
            print(it)     
        }
    }

此外,一些内联函数参数不是直接来自函数体,而是来自另一个上下文的lambda表达式参数,
例如来自局部对象或嵌套函数,lambda表达式也不允许非局部返回!
为了标识这种情况,该lambda表达式参数需要用crossinline修饰符:
    inline fun f(crossinline body: () -> Unit) {
        val f = object: Runnable {
            override fun run() = body()
        }           
    }

目前,break和continue在内联的lambda表达式中还不可用,未来kotlin计划支持!

3.类型参数的具体化(Reified type parameters)

实例(类型参数-泛型):
    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)

1.为简化函数调用,内联函数支持类型参数具体化:
    inline fun <reified T> TreeNode.findParentOfType(): T? {
        //用reified修饰符类型参数T,可在函数内访问T,像访问普通类,
        //由于函数是内联的,无需反射,正常操作符如!is和as都能用,
        var p = parent
        while (p != null && p !is T) {
            p = p.parent
        }
        return p as T?
    }
    //该函数调用简洁优雅
    treeNode.findParentOfType<MyTreeNode>()

2.虽然多数情况不需要反射,但仍然可对具体化的类型参数使用:
    inline fun <reified T> membersOf() = T::class.members
    fun main(s: Array<String>) {
        println(membersOf<StringBuilder>().joinToString("\n"))
    }

3.泛型的具体化条件
    普通函数(未标记为内联函数)不能具体化参数!
    在运行时无法表示的类型(类似Nothing虚构类型)不能作为具体化参数的实参!

4.内联属性(Inline properties)

自kotlin 1.1起, inline可修饰[没有幕后字段]属性访问器get/set函数(方法)
在调用处,内联访问器get/set函数,和内联函数一样内联,没什么区别!
1.修饰单个属性访问器:
    val foo: Foo
        inline get() = Foo()

    var bar: Bar
        get() = ……
        inline set(v) { …… }

2.修饰整个属性,将两个访问器都标记为内联:
    inline var bar: Bar
        get() = ……
        set(v) { …… }

简书:http://www.jianshu.com/p/79396a5056d7
CSDN博客: http://blog.csdn.net/qq_32115439/article/d etails/73929039
GitHub博客:http://lioil.win/2017/06/29/Kotlin-inline-fun.html
Coding博客:http://c.lioil.win/2017/06/29/Kotlin-inline-fun.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容