kotlin中lambda的实现和内联函数

前言

通过阅读lambda表达式与Kotlin高阶函数,你应该了解到在kotlin中传递lambda作为函数参数的语法与普通的表达式很相似。这篇文章则带你了解lambda的运作原理以及用来消除lambda带来的运行时开销的内联函数。

通过字节码分析lambda表达式

我们先申明一个高阶函数lambdaFunction,并使用lambda作为实参对齐进行低啊用:

object Lombda {
    @JvmStatic
    fun main(arg: Array<String>) {
        val lambdaFunction = lambdaFunction({ x, y -> x + y })
        println(lambdaFunction)
    }

    private fun lambdaFunction(function: (Int, Int) -> Int): Int {
        return function(2, 3)
    }
}

在Intellij IDEA中将其字节码文件反编译为java文件,代码如下:

public final class Lombda {
   public static final Lombda INSTANCE;

   @JvmStatic
   public static final void main(@NotNull String[] arg) {
      Intrinsics.checkParameterIsNotNull(arg, "arg");
      int lambdaFunction = INSTANCE.lambdaFunction((Function2)null.INSTANCE);
      System.out.println(lambdaFunction);
   }

   private final int lambdaFunction(Function2 function) {
      return ((Number)function.invoke(Integer.valueOf(2), Integer.valueOf(3))).intValue();
   }

   private Lombda() {
      INSTANCE = (Lombda)this;
   }

   static {
      new Lombda();
   }
}

点击Function2进入Functions.kt文件,部分代码截图如下:

在kotlin.jvm.functions包下Functions.kt文件中定义了一系列的接口,这些接口对应于不同参数数量的函数(in为函数的入参,out为函数的返回值)。每个接口定义了一个invoke方法没调用这个方法就会执行函数。一个函数类型的变量就是实现了对应FunctionN接口的实现类的实例,实现类的invoke方法包含了lambda函数体。一个lambda表达式就是一个FunctionN接口的实现。每个lambda表达式都会被编译一个匿名类(除非它是一个内联函数)。一旦实现,编译器就可以避免为每一个表达式都生成一个class文件。如果lambda表达式捕捉了变量,每个被捕捉的变量会在匿名类中有对应的字段。而且每次对lambda表达式的调用都会创建一个该匿名类新的实例。
了解了lambda函数的实现细节,可以发现:每次调用一次lambda都会额外创建一个类,而且如果lambda捕捉了变量,每次调用都会创建一个新的对象。这回带来运行时的额外开销。接下来就由内联函数来解决这个问题

内联函数:消除lambda带来的运行时开销

如果使用inline修饰符标记一个函数,在函数被使用的时候编译器并不会生成函数调用的代码,而是使用函数实现真实的代码替换每一次的函数调用。

也就是说:函数体会被直接替换到函数被调用的地方,而不是正常的被调用

内联函数运作方法

首先我们定义一个内联函数,并对它进行调用

inline fun lambdaFunction(action:(Int,Int)->Unit){
        action(2,3)
 }   
 
fun main() {
        println("调用前")
        lambdaFunction { x, y -> print(x+y) }
        println("调用后")
    }

下面这段代码和main()的作用相同,并且会被编译为同样的字节码

fun _main_(){
        println("调用前")
        print(2+3)
        println("调用前")
    }

其中lambda表达式被内联了。由lambda表达式生成的自己码成为了函数调用者定义的一部分,而不是被包含在一个实现了函数接口的匿名类中。(我个人的理解为:抽取代码块,编译时将代码块填充进指定位置,即调用内联函数的位置)。函数体会被直接替换到函数被调用的地方。
如果在两个不同的位置使用同一个内联函数,并且使用的lambda不同,那么内联函数会在每一个被调用的位置被分别内联。内联函数的代码会被拷贝到使用它的位置,并把不同的lambda替换到其中。

内联函数的限制

并不是所有的lambda函数都可以被内联。
如果lambda在某个地方被保存起来,lambda表达式的代码将不能被内联。
例如:

val la = {x:Int, y:Int -> print(x+y)}
lambdaFunction (la)

以函数类型的变量作为参数调用内联函数,这种情况下lambda并不会被内联。
需要注意的是:使用内联函数只能提高带有lambda参数的函数的性能。对于普通函数而言,JVM已经提供了强大的内联支持,在字节码中,每个函数的实现只会出现一次,并不需要跟内联函数一样,每个调用的地方都拷贝一次。而且,如果函数直接被调用,调用栈会更加清晰易读。
使用内联函数能给我们带来明显的好处:
避免运行时开销(节约函数调用,lambda创建匿名类和实例对象的开销)

结语

至此,kotlin中的lambda和内联函数已经基本分析完毕。kotlin标准库中提供了许多内联函数,以及操作集合的函数。这些函数可以大大减少代码量和工作效率。持续关注我的博客,了解更多kotlin标准库函数。

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

推荐阅读更多精彩内容