美文网首页
Kotlin与Java比较:内联函数

Kotlin与Java比较:内联函数

作者: 程序引力 | 来源:发表于2018-12-10 23:45 被阅读16次

    前言

    Kotlin作为JVM系的语言,起源于Java又不同于Java。通过在语言层面比较两者的区别,可以使得开发者能够快速学习,融会贯通。

    内联函数概念

    内联函数主要是指在编译或加载时被扩展的函数。以一个简单的例子进行理解:

    fun main(){
        x = 1
        y = 2
        z = add(x, y)
    }
    
    add(x, y){
        return x + y
    }
    

    如果将add方法作为内联函数处理,则add方法会在被调用的地方进行扩展,进而变成如下形式:

    fun main(){
        x = 1
        y = 2
        z = x + y
    }
    

    也就是说,这像是将内联函数add的代码复制到了调用的地方。

    那为何要这么做呢?因为在程序语言中,调用方法需要将方法入栈,然后执行点会移动到栈中的方法处,执行完方法后,方法出栈,回到原来调用方法的地方继续执行。这一过程需要有时间和空间的开销。如果同一个方法被调用得非常频繁,那么会对性能造成一定影响。此时如果将该方法以内联函数的形式进行处理,即在编译或加载时最对进行展开,则避免了这部分的开销。

    内联函数使用

    • Java
      笔者查阅了一些资料,了解到在Java语言层面不支持内联函数,即没有任何关键字可以表示某个函数是内联函数。但是如果将方法声明为静态的或者final的,在编译成字节码文件后,JVM可能对字节码中的相应方法进行优化,即对其进行展开。但这一切仅仅是可能,是否优化以及怎样优化由JVM决定。

    • Kotlin
      除了调用方法的开销,在Kotlin的高阶函数中,被传入的函数都能访问外部函数的变脸(即闭包)。这些使得内存分配与虚拟调用引入性能开销。为此,Kotlin提供了关键字inline对方法进行声明,表示内联函数,如:

    inline fun <T> lock(lock: Lock, body: () -> T): T { …… }
    

    inline 修饰符影响函数本身和传给它的 lambda 表达式:所有这些都将内联到调用处。
    内联函数的适用场景:

    • 方法被频繁调用
    • 方法内语句少

    内联函数优点:可能提升性能
    内联函数缺点:生成的代码增加

    内联的 lambda 表达式只能在内联函数内部调用或者作为可内联的参数传递, 但是对于不希望被内联的函数,可以使用关键字noinline进行声明。这些函数可以以任何我们喜欢的方式操作:存储在字段中、传送它等等。

    内联函数返回

    由于Java在语言层面不支持内联函数,故此部分内容以Kotlin为基础进行介绍。
    若要在lambda表达式中返回一个值,有两种方式,分别是返回表达式中最后一个语句,或者通过return添加标签的形式,例如:

    ints.filter {
        val shouldFilter = it > 0 
        shouldFilter
    }
    
    ints.filter {
        val shouldFilter = it > 0 
        return@filter shouldFilter
    }
    

    在Kotlin中,lambda表达式内部禁止仅使用return进行返回,即不添加任何标签。因为 lambda 表达式不能使包含它的函数返回:

    fun foo() {
        ordinaryFunction {
            return // 错误:不能使 `foo` 在此处返回
        }
    }
    

    但是如果 lambda 表达式传给的函数是内联的,该 return 也可以内联,所以它是允许的:

    fun foo() {
        inlined {
            return // OK:该 lambda 表达式是内联的
        }
    }
    

    这种返回(位于 lambda 表达式中,但退出包含它的函数)称为非局部返回。

    内联属性

    inline 修饰符可用于没有幕后字段的属性的访问器。 可以标注独立的属性访问器:

    val foo: Foo
        inline get() = Foo()
    
    var bar: Bar
        get() = ……
        inline set(v) { …… }
    

    同时,也可以标注整个属性,将它的两个访问器都标记为内联:

    inline var bar: Bar
        get() = ……
        set(v) { …… }
    

    在调用处,内联访问器如同内联函数一样内联。

    相关文章

      网友评论

          本文标题:Kotlin与Java比较:内联函数

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