美文网首页Android
Kotlin入门(六):内联方法

Kotlin入门(六):内联方法

作者: 无余 | 来源:发表于2018-04-05 05:39 被阅读66次

    使用高阶方法会造成一些强制性的开销:内存分配和调用都是开销。不过,在许多情况下有些开销是可以避免的。

    例子:
    现在把吃饭分为三个步骤:1.拿起筷子 2.吃 一口3.放下筷子
    1.情况一:每吃一口都要放下筷子,即拿、吃、放、拿、吃、放...
    操作顺序:

    1、2、3;  1、2、3; 1、2、3...
    

    这种情况不是饭菜不合胃口、就是吃撑了。

    2.情况二:每吃若干口才放下筷子。
    操作顺序:

    1、2、2...2、3; 1、2、2...2、3;... 1、2、2...2、3; 
    

    明显这才是正确操作,能省不少力气。


    在JVM中也有类似的情况,JVM中每个线程都有一个虚拟机栈,每个方法的从调用到完成,对应着入栈、出栈的过程,如果将一方法分为多个方法时,就会有更多的入栈、出栈的开销,使用内联方法能有效减少这部分开销。

    • 例一
      现在我们要打印一个人,分三部分打印:头、身体和脚,其中身体构造复杂,又要分三部分
      1-1
    fun main(args: Array<String>) {
        //printHead
        println("head")
        //printbody
        println("body1")
        println("body2")
        println("body3")
        //printFoot
        println("foot")
    }
    

    现在我们想把打印身体这部放到一个单独的方法中,使代码更清晰:
    1-2

    fun main(args: Array<String>) {
        //printHead
        println("head")
        //printbody
        printBody()
        //printFoot
        println("foot")
    }
    
    fun printBody() {
        println("body1")
        println("body2")
        println("body3")
    }
    

    不过这样一来多了部分开销,这时内联方法可以帮我们避免这部分开销。

    • inline
      定义内联方法需使用“inline”关键字修饰
      1-3
    inline fun printPerson(printBody: () -> Unit) {
        //printHead
        println("head")
        //printbody
        printBody()
        //printFoot
        println("foot")
    }
    fun main(args: Array<String>) {
        printPerson {
            println("body1")
            println("body2")
            println("body3")
        }
    }
    

    编译过程会帮我们把1-3转换为1-1的样子,提高了性能的同事又能保证结构清晰。

    • 例二
      这是官方的例子,刚学习的时候,因为没用过Lock,一时间没反应过来,还以为是Kotlin专有的,没怎么看懂。于是搜索了下其它资料,发现基本都是照着抄一遍,还得靠自己。

    如何才能将代码

    lock(l) { foo() }
    

    实现这样的效果:

    l.lock()
    try {
        foo()
    }
    finally {
        l.unlock()
    }
    

    可以通过内联方法实现:

    inline fun <T> check(lock: Lock, body: () -> T): T {
        lock.lock()
        try {
            return body()
        } finally {
            lock.unlock()
        }
    }
    

    调用:

        var lock = ReentrantLock()
        check(lock) {print("hello")}
    

    完整样例:
    开启两个线程,通过加同一把锁实现同步:

    fun main(args: Array<String>) {
        var lock = ReentrantLock()
        Thread() {
            check(lock) {
                for (i in 1..5) {
                    TimeUnit.SECONDS.sleep(1)
                    println("111")
                }
    
            }
        }.start()
    
        Thread() {
            check(lock) {
                for (i in 1..5) {
                    TimeUnit.SECONDS.sleep(1)
                    println("222")
                }
            }
        }.start()
    }
    
    inline fun <T> check(lock: Lock, body: () -> T): T {
        lock.lock()
        try {
            return body()
        } finally {
            lock.unlock()
        }
    }
    

    输出:

    111
    111
    111
    111
    111
    222
    222
    222
    222
    222
    
    • noinline
      inline 方法中的方法参数默认是inline类型的,如果想过滤掉inline特性,可用noinline修饰。
    inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) {
        // ...
    }
    
    • lambda与return
      1.普通方法的lambda模块中禁止使用return
    fun foo() {
        ordinaryFunction {
            return // ERROR: can not make `foo` return here
        }
    }
    

    2.inline方法的lambda模块可用使用return

    fun foo() {
        inlineFunction {
            return // OK: the lambda is inlined
        }
    }
    
    fun hasZeros(ints: List<Int>): Boolean {
        ints.forEach {
            if (it == 0) return true // returns from hasZeros
        }
        return false
    }
    

    这么设计很好理解,普通方法的lambda模块其实是另一个方法的方法体,你在本方法中调用另一方法中的return算是怎么回事!反而容易误导自己,干脆禁止了事。
    而inline方法可认为是本方法的一部分,外面那层大括号包装可当它不存在,使用return就显得和自然了。
    3.那么想要在普通方法lambda return怎么办?只需在return后加“@方法名”即可:

    fun f() {
        test {
            return@test
        }
    }
    
    fun test(action: () -> Unit) {}
    

    4.inline 类型的方法参数不能直接用于赋值,要想用于赋值得用crossinline 修饰

    inline fun f(crossinline body: () -> Unit) {
        val f = object: Runnable {
            override fun run() = body()
        }
    }
    
    • reified
      泛型只在编译时起作用,在运行时已经被擦除,所以泛型标记是没法当做对象使用的。
      不过在Kotlin中,reified可修饰inline方法中的泛型,对其进行反射操作。
    inline fun <reified T> membersOf() = T::class.members
    
    fun main(s: Array<String>) {
        println(membersOf<User>())
    }
    
    class User() {
        var name = "mao"
    }
    

    相关文章

      网友评论

        本文标题:Kotlin入门(六):内联方法

        本文链接:https://www.haomeiwen.com/subject/svolhftx.html