在 Kotlin kotest 中,数字装箱对于小于 128 的数字给出 false

问题描述 投票:0回答:1

Kotlin 文档包含以下信息。

对 a 的所有可空引用实际上都是同一个对象,因为 JVM 对 -128 到 127 之间的整数应用了内存优化。它不适用于 b 引用,因此它们是不同的对象。

我编写了主要函数和测试代码来检查上述内容。 这时候,每个结果都不同了。

我使用 kotest 对此进行了测试。

class Ch7Test: StringSpec({ "nullable Int between -128 and 127 should be caching" { val boxedA: Int? = 100 val anotherBoxedA: Int? = 100 assertSoftly { (boxedA === anotherBoxedA) shouldBe true } } })
测试结果失败。

expected:<true> but was:<false> Expected :true Actual :false
但是当我在main函数中测试时,结果与测试结果不同。

fun main() { val boxedA: Int? = 100 val anotherBoxedA: Int? = 100 println("debug: ${boxedA === anotherBoxedA}") // output was "debug: true" }
为什么逻辑一样,结果却不一样?

kotlin caching boxing kotest
1个回答
0
投票
实际发生的事情有点被 DSL 语法所掩盖,但本质上,当您在

"foo bar bar" { ... }

 中执行 
StringSpec
 时,您正在调用 
String.invoke 中声明的
StringSpecRootScope

operator fun String.invoke(test: suspend StringSpecScope.() -> Unit)
这里重要的部分是,您传递的 lambda 实际上是一个 

suspend

 lambda。换句话说,装箱和测试发生在协程中。

显然,协程中的拳击是不同的。不是调用像

Integer.valueOf

 这样的 Java 装箱方法,而是调用 
this file 中的方法之一。对于Int
s,它是:

internal fun boxInt(primitive: Int): java.lang.Integer = java.lang.Integer(primitive)
如您所见,这直接创建了一个新的 

Integer

 对象(在 Java 中为 
new Integer(primitive)
)。如果它使用了 
Integer.valueOf
,那么就会使用整数缓存。

根据添加此文件的

commit

这些方法是原始包装类的非常薄的包装 构造函数。

它们由返回原语的协程代码使用,这样 HotSpot 能够完全丢弃分配。

#KT-26591已修复

所以这实际上是专门针对HotSpot JVM的优化。 JVM 无法在每种情况下消除分配(否则您的测试不会失败),但总的来说,这为协程代码提供了净收益。

也就是说,我不熟悉 JVM 内部结构,无法准确解释显式分配

Integer

 实际上如何使 JVM 
优化 分配。

© www.soinside.com 2019 - 2024. All rights reserved.