美文网首页Kotlin编程KotlinKotlin精选
Kotlin边用边学:Inline Functions的适用场景

Kotlin边用边学:Inline Functions的适用场景

作者: 朱和 | 来源:发表于2018-07-06 08:33 被阅读1次
kotlin+android-360.jpg

划重点(Takeaway):

  • Collection自己提供的处理函数(forEach/map...)都支持inline,能用就别自己写循环
    • 在严格执行了上条后,你基本上就不怎么需要了解/写inline functions了
  • 升级第三方库后,务必重新编译你的项目
  • 自己写for/while循环,且循环的N值很大时,是使用inline function的一个切入点
  • 大函数不使用inline
  • 理解不了的时候看反编译生成的Java有奇效
  • 看源代码可以增进了解

基本介绍

Inline functions,中文大概就是内联/内嵌函数,字面的意思就是把内部(偷偷的)把被调用函数的代码连接(Copy)过来,具体看下代码和反编译的结果:

源代码:

fun main(args: Array<String>) {
    val localGreeting = "Hello from main"

    Demo().withPublicField { println(localGreeting) }
}

class Demo() {
    val title = "title in demo"

    inline fun withPublicField(otherFun: () -> Unit) {
        println("Call from withPublicField, title: $title")
        otherFun()
    }
}

查看反编译的Java:(IntelliJ IDEA/Android Studio Tools -> Kotlin -> Show Kotlin Bytecode,别忘了点击Decompile按钮)

public final class MainKt {
   public static final void main(@NotNull String[] args) {
      final String localGreeting = "Hello";
      (new Demo()).withPublicField((Function0)(new Function0() {
         ...
         public final void invoke() {
            String var1 = localGreeting;
            System.out.println(var1);
         }
      }));
   }
}

public final class Demo {
   private final String title = "title_private";

   @NotNull
   public final String getTitle() { return this.title; }

   public final void withPublicField(@NotNull Function0 otherFun) {
      String var2 = "Call from withPublicField, title: " + this_$iv.getTitle();
      System.out.println(var2);
      otherFun.invoke();
   }
}

由于Java的function不是first-class member,所以其高阶函数(higher-order function)的使用,实际上存在着封装function成对应的wrapper class的实例,并对可见范围内(closure)的变量存在引用/访问。这实际上是以一定的资源损耗换来的便利性。对于我们上述的代码:

  • 第4行(Function0)(new Function0() {...}即是function转化成wrapper class实例

  • 第7行 String var1 = localGreeting;即是对可见范围变量的引用

在平常使用中,这个损耗是微小可忽略的,但如果是被一个N次(N值很大)循环中调用,那积少成多的损耗就是一个值得重视的问题了。

对于这个问题,Kotlin的处理方式是直接在编译期将原来的higher-order function的执行/实现代码,copy到调用处。具体看代码(唯一的改动点:原函数前添加了inline关键字):

    inline fun withPublicField(otherFun: () -> Unit) {
        ...
    }

然后查看生成的Java二进制:

    public final class MainKt {
       public static final void main(@NotNull String[] args) {
          String localGreeting = "Hello";
          Demo this_$iv = new Demo();
          String var3 = "Call from withPublicField, title: " + this_$iv.getTitle();
          System.out.println(var3);
          System.out.println(localGreeting);
       }
    }

注意对比两次生成的java代码:原先的L4~L10,现在的L4~L7。可以看到,新的Java代码很简单粗暴的把withPublicField的实现代码copy进入了main函数(自然移除了对withPublicField的调用)。

好处

  • 不需要额外创建wrapper class instance
  • 不需要维护可见范围内的变量引用/调用

代价肯定有的,不然就所有的方法都设成inline不就完了:-)

使用注意点

任何inline function的修改,必须重新编译后才生效

这个从之前反编译的代码可以推断出,毕竟调用inline function的代码已经被抹除,替换成了inline function的实现代码。重新编译才能再次重复这个替换过程。这个对于调用第三方库的inline函数尤其需要注意。

private变量

之前我们看到植入的代码中有这么一行代码(L5):

String var3 = "Call from withPublicField, publicTitle: " + this_$iv.getPublicTitle();

这里,getPublicTitle()如果是个private会怎样,相信大家可以猜测到。具体可以用代码验证:

class Demo() {
    private val title = "title in demo"
    ...
}

修改成private可见性后,代码编译出错(this_$iv.getPublicTitle()在main函数是不可见了)

当然inline函数的写法,除了这个变量的可见性还有其他,譬如:Non-local returns,但只要理解了其编译时的copy行为后,很多就水落石出了。(好的IDE其实还是给了很多有用的提示的,所以尽管写,让IDE去操这个心吧)

源代码查看/验证

Iterator.forEach

public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {
    for (element in this) action(element)
}

可以看到,自带的Collection的函数都早已支持了inline了。所以,能使用自带的Collection函数就别写自己的循环了。

Bonus

相关文章

  • Kotlin边用边学:Inline Functions的适用场景

    划重点(Takeaway): Collection自己提供的处理函数(forEach/map...)都支持inli...

  • 边学边用,边用边学

    我一直想学很多东西,却一直没学好,甚至还没开始真正学习。 大学的时候,我就想学很多东西。想学习谈判,学习演讲,写一...

  • Kotlin边用边学:require / check / ass

    Takeway(划重点) require对外、check对内,组成了协议的前置条件 assert是协议的后置条件k...

  • Kotlin - Inline Functions 1

    Inline Basics Inline or Inlining,我们更经常听到的词是方法内联或者内联函数。在大多...

  • 边学边用

    7月14日《人生效率手册》5 书里说到,如果我们不能将所学的理论与实践相结合,换句话说,在实践场景中,无法调用知识...

  • 边学边用

    2020.08.16 星期天 阴雨天 早上去辅导的时候,马老师给学生买了高中语法书,给我...

  • 边学边用

    分享424 有这么一句话:有人不让你成功,但是没有办法不让你成长。成长是自己的事情,成长应该成为每一个人的状态。 ...

  • 边学边用

    我阅读的书目是《致青年老师》 读书笔记分享:在读《致青年老师》第三辑《心里装着学生》,想学生之所想,急学生之所急,...

  • Kotlin边用边学:别把Extension Function玩

    Key Takeaways(划重点) 设计原则不要忘 扩展是个补锅匠 定向输出是好手 升级外挂要当心 这里假定你对...

  • markdown边学边用

    ```代码块``` ~~删除线~~ 有序列表 数字+英文句号 后面还有空格 反斜杠转义特殊字符\``` *** 分...

网友评论

    本文标题:Kotlin边用边学:Inline Functions的适用场景

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