首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Kotlin作用域函数块是否有效地内联?

Kotlin作用域函数块是否有效地内联?
EN

Stack Overflow用户
提问于 2022-08-06 00:59:58
回答 2查看 132关注 0票数 1

我正在编写一个Kotlin 内联类,以使Decimal4J更方便,而无需实例化任何对象。我担心范围函数可能会创建lambda对象,从而使整个事情变得毫无意义。

考虑下面示例中的函数compareTo

代码语言:javascript
运行
复制
/* imports and whatnot */

@JvmInline
value class Quantity(val basis: Long) {

    companion object {
        val scale: Int = 12
        val metrics: ScaleMetrics = Scales.getScaleMetrics(scale)
        val arithmetic: DecimalArithmetic = metrics.defaultArithmetic
    }

    operator fun compareTo(alt: Number): Int {
        with(arithmetic) {
            val normal = when (alt) {
                is Double     -> fromDouble(alt)
                is Float      -> fromFloat(alt)
                is Long       -> fromLong(alt)
                is BigDecimal -> fromBigDecimal(alt)
                is BigInteger -> fromBigInteger(alt)
                else          -> fromLong(alt.toLong())
            }
            return compare(basis, normal)
        }
    }
}

with(arithmetic)作用域是否在堆中创建lambda?kotlinlang.org上的文档始终将作用域代码称为lambda表达式。有没有办法不用创建对象来使用作用域函数?

EN

Stack Overflow用户

回答已采纳

发布于 2022-08-06 01:22:24

所有内置的作用域函数(包括with )都被标记为inline,这意味着实现直接植入到调用它的代码中。一旦发生这种情况,就可以对lambda调用进行优化。

更具体的说,这里是实施 of with (去掉了Kotlin契约的内容,因为这里不相关)。

代码语言:javascript
运行
复制
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
  return receiver.block()
}

扩展方法在编译时解析语法糖,并且一直是这样,所以这是有效的。

代码语言:javascript
运行
复制
public inline fun <T, R> with(receiver: T, block: (T) -> R): R {
  return block(receiver) // (with `this` renamed by the compiler)
}

所以当我们打电话

代码语言:javascript
运行
复制
operator fun compareTo(alt: Number): Int {
  with (arithmetic) {
    println("Hi :)")
    println(foobar()) // Assuming foobar is a method on arithmetic
  }
}

inline将把它转换成

代码语言:javascript
运行
复制
operator fun compareTo(alt: Number): Int {
  ({
    println("Hi :)")
    println(it.foobar()) // Assuming foobar is a method on arithmetic
  })(arithmetic)
}

任何值其盐分的优化器都可以看到,这是一个被立即评估的函数,所以我们现在应该继续这样做。我们最后的结果是

代码语言:javascript
运行
复制
operator fun compareTo(alt: Number): Int {
  println("Hi :)")
  println(arithmetic.foobar()) // Assuming foobar is a method on arithmetic
}

这就是你一开始就会写的。

所以,tl;dr,编译器是足够聪明的,能够解决它。你不用担心。这是在高级语言中工作的好处之一。

顺便说一下,这不仅仅是抽象的。我只是在我自己的机器上编译了上面的代码,然后对JVM字节码进行反编译,看看它到底做了什么。这有点麻烦(因为JVM必然会有很多噪音),但是没有分配lambda对象,函数只是直接调用println两次。

如果您感兴趣,Kotlin以这个示例函数为例

代码语言:javascript
运行
复制
fun compareTo(alt: Number): Unit {
  return with(arithmetic) {
    println("Hi :)")
    println(foobar())
  }
}

对这个Java来说,在被解压缩之后,

代码语言:javascript
运行
复制
public static final void compareTo-impl(long arg0, @NotNull Number alt) {
    Intrinsics.checkNotNullParameter((Object)alt, (String)"alt");
    long l = arithmetic;
    boolean bl = false;
    boolean bl2 = false;
    long $this$compareTo_impl_u24lambda_u2d0 = l;
    boolean bl3 = false;
    String string = "Hi :)";
    boolean bl4 = false;
    System.out.println((Object)string);
    int n = so_quant.foobar-impl($this$compareTo_impl_u24lambda_u2d0);
    bl4 = false;
    System.out.println(n);
}

虽然有点吵闹,但想法完全一样。所有这些毫无意义的局部变量都将由一个好的JIT发动机来处理。

票数 4
EN
查看全部 2 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73256480

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档