阅读完需:约 4 分钟
内联类
有时候,业务逻辑需要围绕某种类型创建包装器。然而,由于额外的堆内存分配问题,它会引入运行时的性能开销。此外,如果被包装的类型是原生类型,性能的损失是很糟糕的,因为原生类型通常在运行时就进行了大量优化,然而他们的包装器却没有得到任何特殊的处理。
为了解决这类问题,Kotlin 引入了一种被称为 内联类 的特殊类,它通过在类的前面定义一个 inline
修饰符来声明:
inline class Password(val value: String)




内联类必须拥有唯一的一个属性, 并在主构造器中初始化这个属性. 在运行期, 会使用这个唯一的属性来表达内联类的实例
inline class Password(val value: String)
// 'Password' 类的实例不会真实存在
// 在运行期, 'securePassword' 只包含 'String'
val securePassword = Password("Don't try this in production")
这就是内联类的主要功能, 受 “内联” 这个名称的启发而来: 类中的数据被 “内联” 到使用它的地方 (类似于 内联函数 的内容被内联到调用它的地方).
内联类的成员
内联类支持与通常的类相同的功能. 具体来说, 内联类可以声明属性和函数:
inline class Name(val s: String) {
val length: Int
get() = s.length
fun greet() {
println("Hello, $s")
}
}
fun main() {
val name = Name("Kotlin")
name.greet() // 方法 `greet` 会作为静态方法来调用
println(name.length) // 属性的取值函数会作为静态方法来调用
}
但是, 对于内联类的成员存在一些限制:
- 内联类不能拥有 init 代码段
- 内联类的属性不能拥有 后端域变量back field因此, 内联类只能拥有简单的计算属性 (不能拥有延迟初始化属性或委托属性)
内联类的继承
内联类只允许继承接口

interface Printable {
fun prettyPrint(): String
}
inline class Name(val s: String) : Printable {
override fun prettyPrint(): String = "Let's $s!"
}
fun main() {
val name = Name("Kotlin")
println(name.prettyPrint()) // 仍然是调用静态方法
}
- 内联类不能继承其他类
- 内联类是 final 类, 不能被其他类继承


模拟枚举类
内联类也可以用来模拟枚举类,与枚举相比内存占用小,但使用方式类似

inline class State(val ordinal: Int) {
companion object {
val Idle = State(0)
val Busy = State(1)
}
fun values() = arrayOf(Idle, Busy)
val name: String
get() = when (this) {
State.Idle -> "Idle"
State.Busy -> "Busy"
else -> throw IllegalArgumentException()
}
}
inline class Color(val value: UInt) {
companion object {
val Red = Color(0xFFFF0000u)
val Green = Color(0xFF00FF00u)
val Blue = Color(0xFF0000FFu)
}
fun values() = arrayOf(Red, Green, Blue)
val name: String
get() = when (this) {
Red -> "Red"
Green -> "Green"
Blue -> "Blue"
else -> throw IllegalArgumentException()
}
}
fun main() {
State.Busy
Color.Blue
var boxInt = BoxInt(5)
if(boxInt < 10){
println("value is less than 10")
}
val newValue = boxInt.value * 200
println(newValue)
boxInt++
println(boxInt)
}
内联类与类型别名
初看起来, 内联类好像非常象 类型别名. 确实, 它们都声明了一个新的类型, 并且在运行期都表达为各自的底层类型.
但是, 主要的差别在于, 类型别名与它的底层类型是 赋值兼容 的 (与同一个底层类型的另一个类型别名, 也是兼容的), 而内联类不是如此.
也就是说, 内联类会生成一个真正的 新 类型, 相反, 类型别名只是给既有的类型定义了一个新的名字(也就是别名):

typealias NameTypeAlias = String
inline class NameInlineClass(val s: String)
fun acceptString(s: String) {}
fun acceptNameTypeAlias(n: NameTypeAlias) {}
fun acceptNameInlineClass(p: NameInlineClass) {}
fun main() {
val nameAlias: NameTypeAlias = ""
val nameInlineClass: NameInlineClass = NameInlineClass("")
val string: String = ""
acceptString(nameAlias) // 正确: 需要底层类型的地方, 可以传入类型别名
acceptString(nameInlineClass) // 错误: 需要底层类型的地方, 不能传入内联类
// 反过来:
acceptNameTypeAlias(string) // 正确: 需要类型别名的地方, 可以传入底层类型
acceptNameInlineClass(string) // 错误: 需要内联类的地方, 不能传入底层类型
}