美文网首页Kotlin
简单理解 Kotlin 中的 inline 关键字

简单理解 Kotlin 中的 inline 关键字

作者: hyperNeobie | 来源:发表于2018-09-30 21:02 被阅读0次

    参考:

    inline 这个词其实很好理解,翻译成中文就指内联。

    相信每一个学习 Kotlin 的同学都有用过或者遇见过 inline 这个关键字。即便没有在 Kotlin 中见过那也不是什么问题,本文将带领你理解这个关键词。

    实际上,如果你有学过C++,那你已经知道它是个什么玩意了。

    本文就 Kotlin 中的 inline 进行讲解。

    inline 是如何工作的

    在此之前,我想先谈一谈 inline 与 lambda

    Kotlin 在许多情况下使用编译器技巧来隐藏 Java 的旧语言结构。

    在使用函数作为另一个函数的参数(所谓的高阶函数)比在Java中更自然。

    虽然 Kotlin 好处多多,但在用 lambda 时有一些缺点。由于 lambda 表达式都会被悄悄的编译成一个匿名类。这也就意味着需要占用内存。如果短时间内 lambda 表达式被多次调用,大量的对象实例化就会产生内存流失(Memory Churn)。

    所以为了避免这种情况,我们就可以使用 inline

      inline fun inlined(getString: () -> String?) = println(getString())
    
      fun notInlined(getString: () -> String?) = println(getString())
    

    上面的代码中,一个是被 inline 修饰的内联函数,另一个不是。虽然他们的业务解决结果完全相同,都是打印getString(),但如果你实际调用了这两个函数,从反编译后的Java代码中就可以看到很大的差异。

    实际调用:

      fun test() {
        var testVar = "Test"
    
        notInlined { testVar }
    
        inlined { testVar }
    }
    

    相应的反编译的Java代码:

      public static final void test() {
        final ObjectRef testVar = new ObjectRef();
        testVar.element = "Test Variable";
    
        // notInlined:
        notInlined((Function0)(new Function0(0) {
            public Object invoke() {
                return this.invoke();
            }
    
            @NotNull
            public final String invoke() {
               return (String)testVar.element;
            }
        }));
    
        // inlined:
        String var3 = (String)testVar.element;
        System.out.println(var3);
    

    从反编译的代码来看,我们并没有找到实际调用inlined函数的迹象。

    notInlined 使用 Function0 匿名类作为参数调用了该函数。为了遵守Function0接口的协议所以就使用invoke()实现了该方法。由于接口是通用的,因此生成另外的方法,即所谓的桥接方法。

    通过上面的示例,你应该大概也看明白了

    inline 的工作原理就是将内联函数的函数体复制到调用处实现内联。

    inlined 函数内的(也就是 inlined 函数等号右边的) printlin(),被拿去直接 System.out.println()

    所以如果我们的方法比较大或者调用的比较多的话,那么编译器生成的代码量就会变大。

    值得注意的是,如果方法变得足够大,过度使用 inline 可能会妨碍或停止 Hotspot 优化(例如方法内联)。默认情况下, Hotspot 不会内联大于35个字节的方法。

    我们不应该内联所有功能。而且官方也不建议这样做。

    在 kotlin-style-guide 这个库里的 issue 中找到个来自 Kotlin 贡献者的建议,
    原文是:「Functions should only be made inline when they use inline-only features like inlined lambda parameters or reified types.」意思就是说:inline 关键字应该只用在需要内联特性的函数中,比如高阶函数作为参数和具体化的类型参数时。

    当然了,官方也都是挺遵守这个规定的。比如在 Anko 库中,一个我很爱用的内联函数 bg 就有遵守着这个规定:

      @PublishedApi
      internal var POOL = newFixedThreadPoolContext(2 * Runtime.getRuntime().availableProcessors(), "bg")
    
      inline fun <T> bg(crossinline block: () -> T): Deferred<T> = async(POOL) {
        block()
    }
    

    要稍微注意一下的是 POOL 这个变量前面有 @PublishedApi 以及 internal 修饰着,这是因为内联函数体中不能直接访问到其外部类的成员,所以需要声明访问的成员为 internal 并且使用@PublishedApi做注解。

    相信看到这里,你已经对 inline 有了基本了解。

    相关文章

      网友评论

        本文标题:简单理解 Kotlin 中的 inline 关键字

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