0. 引言
前一段时间,kotlin 发布了 1.5.0 Release 版本,其中更改了 inline class
的语法方式,将关键字 inline
改为了 value
,同时原先的 inline
关键字废弃,在这个版本之后使用会有错误提示。
现在,如果需要声明一个内联类,需要使用下面的方式:
@JvmInline // 使用 Jvm 必须要用这个注解
value class Password(val s: String)
如果还不了解内联类的特性,可以参考 Java 中的 Integer
和 int
的关系,内联类的主要实现是通过自动装箱和拆箱完成的。在 kotlin 中它由编译器自动完成。
在本篇文章中,我们将会研究 inline class 是什么, 它的工作原理是什么以及在使用它的时候我们如何去权衡选择。
1. Inline classes
内联类是仅有一个值的价值类的子集。您可以将它们用作特定类型值的包装,而无需使用内存分配产生的额外开销。
上述是 kotlin 官方对内联类的说明,直接看这句话可能有点抽象,其实它的工作原理和 Java 中包装类类似,在需要类型信息的时候,使用这个类,而在作为参数传输的时候,进行拆箱。
这样做的好处是作为一个包装类,如果包装的是基础类型,那么它在 JVM 处理的时候,很可能从 堆上分配
转换成 栈上分配
,这样可以减少许多不必要的开销。例如:当我们创建一个基本类型的局部变量(即函数内定义的函数参数和变量)时 - 如int、float、boolean - 这些值被存储在部分 JVM 内存堆栈中。将这些基础类型的值存储在堆栈上所涉及的性能开销并不大。
在另一方面,每当我们实例化一个对象时,该对象实例就存储在 JVM 堆上了。我们在存储和使用对象实例时会有性能损失 - 堆分配和内存提取的性能代价很高。虽然看起来每个对象性能开销微不足道,但是累积起来,它对代码运行速度产生严重的影响。
通过内联类的拆包,可以让 JVM 上的堆上分配尽可能的转换为栈上分配,让程序的效率更高。
2. 内联类的使用
现在在 kotlin 中可以这样定义一个内联类:
// kotlin 1.3 - 1.4
inline class Hours(val value: Int)
// kotlin 1.5
@JvmInline // 使用 Jvm 必须要用这个注解
value class Hours(val value: Int)
当在使用的时候,可以直接创建一个 Hours 对象,例如:
val h = Hours(9)
就 JVM 而言,实际上相当于下面这样的代码:
val h = 9
可以看到,通过一个包装类让它变成了基础变量,以支持 JVM 的栈上分配。
同