我正在编写一个Kotlin 内联类,以使Decimal4J更方便,而无需实例化任何对象。我担心范围函数可能会创建lambda对象,从而使整个事情变得毫无意义。
考虑下面示例中的函数compareTo。
/* 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表达式。有没有办法不用创建对象来使用作用域函数?
发布于 2022-08-06 01:22:24
所有内置的作用域函数(包括with )都被标记为inline,这意味着实现直接植入到调用它的代码中。一旦发生这种情况,就可以对lambda调用进行优化。
更具体的说,这里是实施 of with (去掉了Kotlin契约的内容,因为这里不相关)。
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
return receiver.block()
}扩展方法在编译时解析语法糖,并且一直是这样,所以这是有效的。
public inline fun <T, R> with(receiver: T, block: (T) -> R): R {
return block(receiver) // (with `this` renamed by the compiler)
}所以当我们打电话
operator fun compareTo(alt: Number): Int {
with (arithmetic) {
println("Hi :)")
println(foobar()) // Assuming foobar is a method on arithmetic
}
}inline将把它转换成
operator fun compareTo(alt: Number): Int {
({
println("Hi :)")
println(it.foobar()) // Assuming foobar is a method on arithmetic
})(arithmetic)
}任何值其盐分的优化器都可以看到,这是一个被立即评估的函数,所以我们现在应该继续这样做。我们最后的结果是
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以这个示例函数为例
fun compareTo(alt: Number): Unit {
return with(arithmetic) {
println("Hi :)")
println(foobar())
}
}对这个Java来说,在被解压缩之后,
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发动机来处理。
发布于 2022-08-06 04:38:22
只是一些额外的信息,以帮助清理术语,导致您的混乱。
单词“lambda”被定义为编写函数的语法。这个词并不描述函数本身,因此lambda这个词与函数对象是否被分配无关。
在Kotlin中,可以选择多个不同的语法来定义或引用函数。兰博达只是其中之一。
// lambda assigned to variable
val x: (String) -> Unit = {
println(it)
}
// anonymous function assigned to variable
val y: (String) -> Unit = fun(input: String) {
println(input)
}
// reference to existing named function assigned to variable
val z: (String) -> Unit = ::println
// lambda passed to higher order function
“Hello World”.let { println(it) }
// anonymous function passed to higher order function
“Hello World”.let(fun(input: Any) { println(input) })
// reference to existing named function passed to higher order function
“Hello World”.let(::println)
// existing functional reference passed to higher order function
“Hello World”.let(x)实际上,不存在可以传递的lambda对象。该对象是一个可以使用上述任何语法定义的函数。一旦函数引用存在,用于创建它的语法就无关紧要了。
使用内联高阶函数,作为标准库作用域函数,编译器优化了函数对象的创建。在我上面的例子中的四个高阶调用中,前三个将编译为相同的内容。最后一个有一点不同,因为函数x已经存在,所以在内联代码中调用的将是x本身。它的内容不会被挂起,直接在内联代码中调用。
在高阶内联函数调用中使用lambda语法的优点是,它使您能够为外部作用域(非本地返回)使用关键字,例如return、continue或break。
https://stackoverflow.com/questions/73256480
复制相似问题