我试图理解内联类的概念-它们是在运行时内联的单一属性的简单对象包装。 这意味着该类的实际初始化不是在运行时发生的
我试图编写简单的测试,该测试将在JUnit测试期间直接显示我的上述解释,如下所示:
companion object {
private const val NAME = "JACK"
}
inline class NameInlineClass(val value: String)
@Test
fun unwrapping() {
val nameInlineClass = NameInlineClass(NAME)
val name = nameInlineClass
assertEquals(name, NAME)
}
不幸的是,该测试失败了,这使我想到一个问题,为什么在assertEquals()
期间不比较实际的未包装的String值,而是在比较实际的内联类(在运行时应对其进行包装)?
答案 0 :(得分:5)
您可能想做的是val name = nameInlineClass.value
,但我将尽力解释该错误。
请参阅文档中的Representation(包括代码示例):
在生成的代码中,Kotlin编译器为每个内联代码保留一个包装器 类。内联类实例可以在运行时表示为 包装器或作为基础类型。这类似于Int的方式 表示为原始int或包装Integer。
这意味着只要您不显式引用包装对象或其类型,就不会将value装箱。我们可以通过检查字节码(将其反编译为Java以提高可读性)进行检查:
// kotlin source
fun unwrapping() {
val nameInlineClass = NameInlineClass(NAME)
val name = nameInlineClass // this line gets dropped by compiler by the way
assertEquals(name, NAME)
}
// java representation of bytecode
public final void unwrapping() {
String nameInlineClass = NameInlineClass.constructor-impl("JACK");
Assert.assertEquals(NameInlineClass.box-impl(nameInlineClass), "JACK");
}
我不会粘贴整个生成的NameInlineClass
主体,但是constructor-impl
是静态方法,仅检查null
中的value
,并且box-impl
创建包装对象。
您可以看到nameInlineClass
确实是String
-这意味着内联有效,没有分配额外的对象。
仅当引用nameInlineClass
而不是nameInlineClass.value
时,编译器才确定该对象需要表示形式,并使用包装器NameInlineClass
类将值“装箱”。