https://openjdk.java.net/jeps/359概述了新的Java功能,该功能可能会/将在某些将来的Java版本中实现。 JEP建议使用一种新型的“班级”:记录。 JEP中的示例内容如下:
record Range( int lo, int hi) { public Range { if (lo > hi) /* referring here to the implicit constructor parameters */ throw new IllegalArgumentException(String.format( "(%d,%d)" , lo, hi)); } }
本质上,一条记录将是一个类,该类只打算在构造函数中设置final
字段。 到今天为止,JEP还允许类具有的任何其他成员,但从本质上说,记录就是记录,它是纯数据,核心可能没有功能。 记录的描述简短而切合实际,并且消除了很多我们需要用Java 13或更少的语言编码此类或将要实现的版本记录的样板。 上面使用常规Java的代码如下所示:
public class Range { final int lo; final int hi; public Range( int lo, int hi) { if (lo > hi) /* referring here to the implicit constructor parameters */ throw new IllegalArgumentException(String.format( "(%d,%d)" , lo, hi)); this .lo = lo; this .hi = hi; } }
考虑到我的Java :: Geci代码生成项目,这对于代码生成器来说是一个巨大的挑战,它弥合了当今与新功能在所有生产平台上都可用的那一天之间的差距。
因此,我开始考虑如何开发此生成器,并且遇到了一些问题。 Java :: Geci框架只能将可编译项目转换为另一个可编译项目。 它不能像将不完整的源代码转换为完整版本的其他代码生成器那样工作,该源代码无法将不完整的源代码(未经代码生成器的修改就无法编译)。 这是因为Java :: Geci在测试阶段起作用。 为了进入测试阶段,必须先编译代码。 这是一个众所周知的折衷方案,是一项设计决策。 在大多数情况下,当Java :: Geci有用时,这很容易解决。 另一方面,我们得到的好处是生成器不需要配置管理,例如读取和解释属性或XML文件。 它们仅提供API,并且从测试中调用它们的代码通过它配置生成器。 最大的优点是,您甚至可以通过生成器调用的方法引用,lambda或对象实例的形式提供回调,以便这些生成器在其工作的某些方面可以具有完全开放的结构。
为什么在这种情况下如此重要? 记录生成相当简单,不需要任何复杂的配置,事实上,它根本不需要任何配置。 另一方面,可compilable -> compilable
compilable -> compilable
限制正在影响它。 如果您开始使用Java 8和Java :: Geci创建记录,那么您的手动代码将如下所示:
@Geci ( "record" ) public class Range { final int lo; final int hi; }
这不会编译,因为在代码生成开始之前的第一次编译时,默认构造函数不会初始化字段。 因此,这些字段不能为final
:
@Geci ( "record" ) public class Range { int lo; int hi; }
运行发电机,我们将得到
package javax0.geci.tests.record; import javax0.geci.annotations.Geci; @Geci ( "record" ) public final class Range { final int lo; final int hi; //<editor-fold id="record"> public Range( final int lo, final int hi) { this .lo = lo; this .hi = hi; } public int getLo() { return lo; } public int getHi() { return hi; } @Override public int hashCode() { return java.util.Objects.hash(lo, hi); } @Override public boolean equals(Object o) { if ( this == o) return true ; if (o == null || getClass() != o.getClass()) return false ; Range that = (Range) o; return java.util.Objects.equals(that.lo, lo) && java.util.Objects.equals(that.hi, hi); } //</editor-fold> }
这个生成器实际上所做的是
- 它生成构造函数
- 将JEP的要求将类和字段转换为
final
- 生成字段的吸气剂
- 为该类生成
equals()
和hashCode()
方法
如果该类的void
方法具有与该类相同的名称(尽管不区分大小写),例如:
public void Range( double hi, long lo) { if (lo > hi) /* referring here to the implicit constructor parameters */ throw new IllegalArgumentException(String.format( "(%d,%d)" , lo, hi)); }
然后发电机将
- 从生成的构造函数中调用该方法,
- 修改方法的参数列表以匹配当前字段列表。
public void Range( final int lo, final int hi) { if (lo > hi) /* referring here to the implicit constructor parameters */ throw new IllegalArgumentException(String.format( "(%d,%d)" , lo, hi)); } //<editor-fold id="record"> public Range( final int lo, final int hi) { Range(lo, hi); this .lo = lo; this .hi = hi; }
请注意,这种生成方法试图表现出JEP中建议的最接近实际record
的可能,并生成可立即转换为新语法的代码。 这就是验证器方法必须与类具有相同名称的原因。 当转换为真实记录时,所有要做的就是删除将方法转换为构造函数的void
关键字,删除参数列表,因为它将隐含在JEP中定义,并删除编辑器折叠之间的所有生成代码。 (也在首次执行生成器时自动生成)。
手动输入的代码的修改是Java :: Geci的新功能,它是由Record生成器的需求触发的,旨在克服可compilable -> compilable
的缺点compilable -> compilable
compilable -> compilable
限制。 后续文章中将详细介绍Java 1.:Geci的下一个1.3.0版本中提供的生成器如何使用此功能。
带走
本文的重点是,即使在Java记录可用之前,也可以将其与Java 8、9一起使用。
翻译自: https://www.javacodegeeks.com/2019/10/java-record.html