Kotlin中的inline关键字 - 内联函数

版权归作者所有,转发请注明出处:https://www.jianshu.com/p/14a837e67dd7

Kotlin中的inline关键字 - 内联函数
Kotlin中的inline关键字 - 内联类

前言

Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,被称之为 Android 世界的Swift,在Google I/O 2017中,Google 宣布 Kotlin 成为 Android 官方开发语言

img.jpg

内联函数

inline关键字所修饰的函数为内联函数

内联函数的作用

尝试去修饰普通函数

inline fun getAmount() = 0.1

这种写法编辑器会有警告提示:
Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types
意味着使用inline只有在修饰一个参数为functional types的函数时候效果最好,也就是我们所说的高阶函数的一种,kotlin中的forEach函数中参数action就是一个函数类型,所以forEach函数使用了inline 修饰

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

inline修饰高阶函数的好处
我们对高阶函数分别使用普通函数声明,和使用内联函数声明

class Test {
    
    //不使用inline 修饰的高阶函数
    fun test(f: () -> Unit) {
        f()
    }

    //使用inline 修饰的高阶函数
    inline fun testInline(f: () -> Unit) {
        f()
    }

    fun call() {
        test {
            print("test")
        }
        testInline {
            print("testInline")
        }
    }
}

转化为Java代码

public final class Test {
   public final void test(@NotNull Function0 f) {
      Intrinsics.checkParameterIsNotNull(f, "f");
      f.invoke();
   }

   public final void testInline(@NotNull Function0 f) {
      int $i$f$testInline = 0;
      Intrinsics.checkParameterIsNotNull(f, "f");
      f.invoke();
   }

   public final void call() {
      this.test((Function0)null.INSTANCE);
      int $i$f$testInline = false;
      int var3 = false;
      String var4 = "testInline";
      boolean var5 = false;
      System.out.print(var4);
   }
}

call()中可以看出,第一行,当调用非内联函数时是直接调用了此函数,并且创建了匿名类Function0用于Lambda函数的调用

this.test((Function0)null.INSTANCE);

call()中的其他代码可以看出,内联函数则是复制了函数体过来,而没有创建匿名类,而是直接嵌入了Lambda函数的实现体

int $i$f$testInline = false;
int var3 = false;
String var4 = "testInline";
boolean var5 = false;
 System.out.print(var4);

当高阶函数没有使用Inline修饰时,调用此函数会直接引用此函数,并且会创建匿名类以实现此函数参数的调用,这有两部分开销,直接调用此函数会创建额外的栈帧以及入栈出栈操作(一个函数的调用就是一个栈帧入栈和出栈的过程),并且匿名类的创建也会消耗性能

使用 Inline 修饰高阶函数是会有性能上的提升

避免内联大型函数

在调用内联函数时,基于上述我们已经知道内联会将Lambda参数函数体直接进行嵌入,避免了函数的引用以及栈帧和对象创建的开销,但是如果Lambda所内联的函数体数量太大,嵌入则会造成调用位置的函数体增长,所以需要 避免内联大型函数

noinline关键字使用

内联Lambad只能在内联函数内部使用,或者作为内联参数进行传递,如果想 将内联Lambad作为普通函数参数进行传递或者存储在字段中则不能使用内联函数声明,或者在声明为内联函数然后将需要传递的Lambda参数指定为noinline

inline fun testInline(f: () -> Unit) {
    testInlineInner(f) //会提示错误,内联Lambad 不能作为普通函数参数进行传递
}

fun testInlineInner(f: () -> Unit) {
    f()
}

如果一个函数中有多个函数参数,并且部分参数时可以直接使用内联方式调用,另一部分则需要进行传递或者赋值,则可以将函数声明为内联函数,内联函数中,不符合内联方式的参数使用noinline修饰

inline fun testInline(f1: () -> Unit, noinline f: () -> Unit) {
    f1()
    testInlineInner(f)
}

fun testInlineInner(f: () -> Unit) {
    f()
}

内联Lambda中使用return

  • 在调用普通高阶函数时,在Lambda中不能直接使用retrun退出lambda表达式,需要进行声明然后退出lambda,inline lambda可以直接return,但是退出的是当前外部函数而不是lambda表达式
fun call() {
    testOrdinary {
        print("testOrdinary")
        return //报错
    }
    testOrdinary {
        print("testOrdinary")
        return@testOrdinary //正常 退出 testOrdinary 
    }
    testInline {
        print("testInline")
        return //正常 退出call()
    }
    testInline {
        print("testInline")
        return@testInline //正常 退出 testInline 
    }
}

crossinline关键字使用

当内联Lambda不是直接在函数体中调用,而是在嵌套函数或者其他执行环境中调用则需要声明为crossinline

inline fun testInline(crossinline callBack: () -> Unit) {
    object : Thread(){
        override fun run() {
            callBack() //执行在object中,需要将callBack声明为 crossinline
        }
    }
}

Reified 类型的参数

reified 是Kotlin关于范型的关键字,从而达到泛型不被擦除,如果需要使用reified 去修饰泛型方法中的泛型类型,则需要使用Inline修饰此泛型方法,因为Inline函数可以指定泛型类型不被擦除,因为内联函数编译期会将字节码嵌入到调用它的地方,所以编译器才会知道泛型对应的具体类型,使用reified 和 Inline适用于泛型方法,并且方法体中需要对泛型类型做以判断的情况

inline fun <reified T> Bundle.plus(key: String, value: T) {
    when (value) {
        is String -> putString(key, value)
        is Long -> putLong(key, value)
        is Int -> putInt(key, value)
    }
}

Inline properties

inline也可以修饰setget方法也会在调用位置进行嵌入代码以收益性能,并且可以直接修饰属性使两个访问器都标记为inline

class Amount(var amount: Long) {
    private val isEmpty: Boolean
        inline get() {
            return amount <= 0
        }

    private val isEmptyNoInline: Boolean
        get() {
            return amount <= 0
        }

    fun test(){
       val amount = Amount(10)
        val isEmpty = amount.isEmpty
        val isEmptyNoInline = amount.isEmptyNoInline
    }
    //转化为Java代码
    public final void test() { 
      Amount amount = new Amount(10L);
      int $i$f$isEmpty = false;
      boolean isEmpty = amount.getAmount() <= 0L; //代码嵌入
      boolean isEmptyNoInline = amount.isEmptyNoInline(); //非嵌入
   }
}

欢迎关注Mike的简书

Android 知识整理

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

推荐阅读更多精彩内容