不久,新的语法功能将出现在即将发布的Java 14-记录中。 在研究了预览文件后 ,我简要地介绍了录音的外观和“吃什么”,然后我敢于将文件改编成俄语以供Habr使用。 谁在乎-欢迎猫。
总结
条目允许您扩展Java的功能。 它们为声明类提供了简洁的语法,这些类是持久不变的数据集的简单载体。
原因和目标
抱怨“ Java太冗长”,而您需要对其进行“仪式”是很常见的。 这样做的原因是仅用于存储特定数据集的类。 为了正确地编写这样的类,您需要编写许多正式的,重复的和容易出错的代码:构造函数,getter和setter,equals(),hashCode(),toString()等。 开发人员有时会作弊,并且不会重新定义equals()和hashCode(),这反过来可能导致异常行为或调试问题。 或者,当开发人员不想声明另一个类时,他们规定了一个替代方法,但并不完全适合,因为它具有“正确的形式”。
开发环境将帮助在该类中注册大多数代码,但是将不帮助开发人员阅读该代码以在数十行样板代码中快速导航并理解该类是普通的数据载体。 Java代码建模标准数据集应易于编写,理解和验证。
乍一看,这些记录似乎旨在减少模板代码。 我们将语义目标置于其中:
“将数据建模为数据” (将数据建模为数据)。 如果语义正确,那么模板代码将自行完成所有操作,而无需开发人员的参与。 毕竟,声明持久性数据集应该容易,清晰和简洁。
没有的目标
我们没有在样板代码中设定“宣战”的目标。 特别是,我们无意使用JavaBean组件的命名约定来解决可变类的问题。 尽管事实上,基于注释的属性,元编程和代码生成通常被建议作为解决此问题的“解决方案”,但添加这些功能也不是我们的目标。
内容描述
条目是Java中一种新型的类型声明。 像枚举一样,写作在功能上受到限制。 它宣布其视图并提供基于该视图的API。 条目不会将API与表示分开,因此很简洁。
该条目包含名称和状态说明。 状态描述声明了该记录的组成部分。 可选地,记录可以具有主体。 例如:
record Point(int x, int y) { }
由于语义记录是简单的数据载体,因此它们会自动接收标准元素:
- 每个州组成部分的私有最终字段;
- 具有相同名称和类型的每个状态组件的公共读取方法;
- 与记录签名匹配的公共构造函数; 它从相应的参数初始化每个字段;
- equals()和hashCode()的实现,它们表示两个记录具有相同的类型并且包含相同的状态,则它们相等。
- toString()的实现,其中包括所有记录组件及其名称的字符串表示形式。
换句话说,记录的呈现完全基于状态的描述。 同样基于记录的状态,生成equals(),hashCode()和toString()。
局限性
记录不能继承任何其他类,也不能声明对象字段,但与状态组件相对应的私有最终字段除外。 任何其他声明的字段都必须是静态的。 这些限制确保状态描述本身可以定义视图。
条目是最终的,不能抽象。 这些限制表明,记录API仅由状态描述定义,以后不能用另一个类或记录进行扩展。
录制组件是最终的。 此限制实现了“默认不变”的原则,该原则已广泛用于数据集。
除上述限制外,记录的行为类似于普通类:它们可以声明为顶级或嵌套的,它们可以是泛型的,可以实现接口。 通过调用new运算符来创建记录。 编写主体可以声明静态方法,静态字段,静态初始化块,构造函数,实例方法,实例初始化块和嵌套类型。 记录和各个状态组件可以被注释。 如果记录是嵌套的,则它是静态的。 这消除了使用嵌套实例可以自动向记录添加状态的情况。
明确声明的条目
尽管在大多数情况下可以接受getter的标准实现以及equals(),hashCode()和toString()方法,但开发人员可以选择覆盖标准实现。 但是,在重写equals / hashCode方法时应格外小心。
特别注意规范构造函数的显式声明,该规范的签名与记录状态的描述匹配。 可以在没有正式参数列表的情况下声明构造函数:在这种情况下,假定它与状态描述一致,并且通过标准地从输出处的相应形式参数(this.X = x)关闭构造函数主体来隐式初始化任何记录字段。 这允许规范构造函数仅检查和调整其参数,并跳过显式字段初始化。 例如:
record Range(int lo, int hi) { public Range { if (lo > hi) throw new IllegalArgumentException(String.format("(%d,%d)", lo, hi)); } }
文法
RecordDeclaration: {ClassModifier} record TypeIdentifier [TypeParameters] (RecordComponents) [SuperInterfaces] [RecordBody] RecordComponents: {RecordComponent {, RecordComponent}} RecordComponent: {Annotation} UnannType Identifier RecordBody: { {RecordBodyDeclaration} } RecordBodyDeclaration: ClassBodyDeclaration RecordConstructorDeclaration RecordConstructorDeclaration: {Annotation} {ConstructorModifier} [TypeParameters] SimpleTypeName [Throws] ConstructorBody
记录组件的批注
如果批注注释适用于组件,参数,字段或方法,则它们可以应用于记录组件。 适用于所有这些组件的广告注释均适用于任何必需元素的隐式声明。
更改记录组件类型的类型注释扩展为所需元素的隐式声明中的类型(例如,构造函数参数,字段声明和方法)。 必需元素的显式声明必须与记录的相应组件的类型完全匹配,但不包括类型注释。
反射API
以下公共方法将添加到
java.lang.Class中 :
- RecordComponent [] getRecordComponents()
- boolean isRecord()
getRecordComponents()方法返回一个数组
java.lang.reflect.RecordComponent ,其中
java.lang.reflect.RecordComponent是一个新类。
此数组的元素与记录的组件相对应,并按照在记录中声明它们的顺序进行排序。 可以从数组中的每个
RecordComponent提取其他信息,包括名称,类型,泛型及其值。
如果将该类声明为记录,则
isRecord()方法将返回
true 。 (类似于
isEnum()方法)。
替代品
记录可以定义为元组的条件形式。 除了记录,我们可以使用结构化元组。 尽管元组提供了表示某些数据集的更轻量级的方法,但结果通常信息较少:
- Java哲学的主要原理是名称很重要 。 类及其元素的名称与其内容相关,而元组及其组件的名称则无关。 也就是说,具有firstName和lastName属性的Person 类比 String和String中的匿名元组更易于理解和可靠。
- 类通过其构造函数支持状态验证,而元组则不支持。 一些数据集(例如数值范围)具有不变量,如果构造函数使用了不变量,则以后可以引用这些不变量。
- 类可能会根据其状态而有行为; 状态和行为的结合使行为本身更加明确和易于访问。 元组只是一个数据集,并不提供这样的机会。
依存关系
记录与
隔离类型配合良好
(JEP 360) ; 记录与孤立的类型一起构成一个构造,通常称为
代数数据类型。 另外,条目本身允许
模式匹配 。 因为记录将其API与状态描述相关联,所以我们最终还可以获取记录的解构模式,并在
switch语句中使用隔离类的信息。