7.1.4 在 Kotlin 中使用 Java 记录类型

原文链接: https://kotlinlang.org/docs/jvm-records.html

7.1.4 在 Kotlin 中使用 Java 记录类型

记录类型(Records)是 Java 中用于存储不可变数据的类。记录类型携带一组固定的值——即记录组件。它们在 Java 中有简洁的语法,省去了编写样板代码的麻烦:

1
2
// Java
public record Person (String name, int age) {}

编译器会自动生成一个继承自 java.lang.Record 的 final 类,其中包含以下成员:

  • 为每个记录组件生成一个 private final 字段
  • 一个带所有字段参数的 public 构造函数
  • 一组实现结构相等性的方法:equals()、hashCode()、toString()
  • 一个用于读取每个记录组件的 public 方法

记录类型与 Kotlin 的数据类非常相似。

在 Kotlin 代码中使用 Java 记录类型

你可以像使用 Kotlin 中带属性的类一样,使用在 Java 中声明了组件的记录类。要访问记录组件,只需像访问 Kotlin 属性那样使用它的名称:

1
2
val newPerson = Person("Kotlin", 10)
val firstName = newPerson.name

在 Kotlin 中声明记录类型

Kotlin 只支持为数据类声明记录类型,并且该数据类必须满足相关要求。

要在 Kotlin 中声明记录类,请使用 @JvmRecord 注解:

注意: 把 @JvmRecord 应用到已有类上不是二进制兼容的变更。它会改变类属性访问器的命名约定。

1
2
@JvmRecord
data class Person(val name: String, val age: Int)

这个 JVM 专用注解可以生成:

  • 类文件中与类属性相对应的记录组件
  • 按 Java 记录命名约定命名的属性访问器方法

该数据类会提供 equals()、hashCode() 和 toString() 方法的实现。

要求

要声明带 @JvmRecord 注解的数据类,它必须满足以下要求:

  • 该类必须位于面向 JVM 16 字节码的模块中(如果启用 -Xjvm-enable-preview 编译器选项,也可以是 15)。
  • 该类不能显式继承任何其他类(包括 Any),因为所有 JVM 记录类型都隐式继承 java.lang.Record。不过,该类可以实现接口。
  • 该类不能声明任何带幕后字段的属性——由相应主构造函数参数初始化的属性除外。
  • 该类不能声明任何带幕后字段的可变属性。
  • 该类不能是局部类。
  • 该类主构造函数的可见性必须与类本身的可见性一致。

启用 JVM 记录类型

JVM 记录类型要求生成的 JVM 字节码目标版本为 16 或更高。

要显式指定它,请在 Gradle 或 Maven 中使用 jvmTarget 编译器选项。

在 Kotlin 中为记录组件添加注解

实验性-通用

在 Java 中,记录组件上的注解会自动传播到幕后字段、getter、setter 和构造函数参数。你可以在 Kotlin 中使用 all 使用处目标来复现这一行为。

例如:

1
2
@JvmRecord
data class Person(val name: String, @all:Positive val age: Int)

当你把 @JvmRecord 与 @all: 一起使用时,Kotlin 会:

  • 把该注解传播到属性、幕后字段、构造函数参数、getter 和 setter。
  • 如果该注解支持 Java 的 RECORD_COMPONENT,还会把注解应用到记录组件本身。

让注解同时适用于记录组件

要让某个注解同时对 Kotlin 属性和 Java 记录组件可用,请在你的注解声明中添加以下元注解:

例如:

1
2
3
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
@java.lang.annotation.Target(ElementType.CLASS, ElementType.RECORD_COMPONENT)
annotation class ExampleClass

现在你可以把 @ExampleClass 应用于 Kotlin 的类和属性,以及 Java 的类和记录组件。

进一步讨论

关于更多技术细节和讨论,请参阅这份 JVM 记录类型的语言提案。