Kotlin 2.5 Value Class 终于能放多个属性了

在 Kotlin 2.5.0-Beta2 之前,内联值类(value class)可以为单个值定义自定义的包装类型。如果类型需要多个属性,就只能使用普通类或数据类。

以 data class 来讲,即使两个实例包含相同的数据,仍然可以通过引用区分它们,哪怕这种区别对该类型并没有实际意义。

这样做可能限制运行时优化,也可能让代码不必要地依赖对象引用是否保持不变。

例如:

kotlin 复制代码
data class Point(val x: Int, val y: Int)

val a = Point(1, 2)
val b = Point(1, 2)
val c = a

println(a == b)  // true:属性值相同
println(a === b) // false:是两个不同的对象
println(a === c) // true:引用同一个对象

对于坐标,我们通常只关心它是不是 (1, 2)。至于它是第一次创建的那个对象,还是后来创建的另一个对象,对"坐标"这个概念没有意义。但普通类和 data class 仍然允许代码使用 === 来观察这种区别。

Kotlin 2.5.0-Beta2 引入了实验性的完整值类(full value classes),这些类型的值由各个属性共同构成,实例没有可供依赖的身份。直接使用 === 比较这些值会被编译器拒绝:你不能依赖它们是不是同一个对象来区分它们。

例如,可以声明一个完整值类 Complex,用实部和虚部两个属性表示复数:

kotlin 复制代码
value class Complex(val real: Double, val imaginary: Double)

与 data class 类似,编译器会为完整值类自动生成 equals()、hashCode() 和 toString()。

不过,有两点与 data class 不同:

  • 不生成 componentN() 函数。但可以使用基于名称的解构,无需额外启用。
  • 不生成 copy() 函数。未来可以改用 copy vars,这个我们暂时不讲。

抽象值类

完整值类可以是抽象的。

与接口不同的是,它们可以定义初始化逻辑,每个子类在构造时都会执行这些逻辑。

例如,下面的 OrderedInterval 值类通过构造参数接收 start 和 end。IntInterval 值类则将这些值存储在自己的 start 和 end 属性中。

kotlin 复制代码
sealed value class OrderedInterval<T : Comparable<T>> protected constructor(
    start: T,
    end: T,
) {
    init {
        require(start <= end)
    }
}

value class IntInterval(
    val start: Int,
    val end: Int
) : OrderedInterval<Int>(start, end)

值对象

完整值类也可以没有属性,可以使用 value object 声明值对象(value objects)。

它适合表示只有一个可能的值、且没有实例身份的类型,例如状态或令牌:

kotlin 复制代码
sealed interface UserResponse

value class UserFound(val user: User) : UserResponse
value object UserUnknown : UserResponse

如何启用

该功能仍处于实验阶段。

如果想尝鲜启用它,需要在构建文件中添加以下编译器选项:

kotlin 复制代码
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xfull-value-classes")
    }
}

类声明的变化

引入这些新值类后,value class 不再一定意味着内联。

内联值类在运行时可能直接使用它包装的值。这种表示方式还可能影响 JVM 方法签名和序列化结果等外部行为。

完整值类则不遵循同样的内联表示规则,因此 Spring 和序列化库等工具对这两种类型的处理方式可能不同。

为了明确区分两者,按照目前的迁移计划,未来编译器将要求使用 inline 修饰符,或采用 @JvmInline value class 的声明方式,显式标记内联值类。仅使用 value class 的声明将不再被视为内联值类。

Kotlin 团队计划分阶段调整这一行为:

  • Kotlin 2.5.20 将对未显式标记的单字段值类发出警告。
  • Kotlin 2.6.0 或 2.7.0 将把该警告升级为错误。

可以使用 -Xinline-value-class-migration=warning 和 -Xinline-value-class-migration=strict 编译器选项,提前为这一变化做好准备。

为库中的类迁移到值类做准备

作为库作者,可以使用 @WillBecomeValue 注解,为将来把库中的普通类迁移到完整值类做准备。当使用者的代码依赖对象身份时,例如使用引用相等比较(===)、同步操作或身份哈希码,编译器会发出警告。这些警告能让使用者在类迁移之前,有时间调整自己的代码。

该注解仍处于实验阶段,需要通过 @OptIn(ExperimentalValueClassApi::class) 显式启用。库的使用者使用带有该注解的类时,不需要额外启用。

相关推荐
恋猫de小郭1 小时前
Flutter Android 性能修复:8 个大核的手机,Raster 居然只跑在 2 个核上?
android·flutter
传奇开心果编程14 小时前
【Jetpack Compose娓娓道来】第29课:状态容器与UI事件建模——把“散装状态”变成“可穷举的契约”
android·kotlin·移动开发·android jetpack·ui框架·开发范式·声明式ui
五彩小白15 小时前
喵呜打卡项目总结
android·团队开发
古法安卓16 小时前
Android-GPU压测工具
android·java·android studio
xcLeigh18 小时前
KingbaseES数据库教程:初识数据库,从安装到跑通第一条SQL
android·数据库·sql·安装·数据库教程·kes
TimeFine21 小时前
AI 编码工作流的设计(二)
android·ai编程
2401_8818283221 小时前
MySQL操作命令
android·mysql·adb
用户60709759149061 天前
AOSP 14 OTA 升级指导(三):三条升级链路——Java API、自研 HAL 与 TR069
android
前端小D1 天前
React Native Android 项目搭建
android·react native·react.js