2020年3月17日,Java正式发布JDK14,现已开放下载。在JDK14中,一共有16个新特性。本文主要介绍其中一个:JEP359:记录官方投诉最致命。早在2019年2月,Java语言架构师BrianGoetz就曾写过一篇文章(http://cr.openjdk.java.net/~briangoetz/amber/datum.html),对Java语言进行了详细的解释和吐槽,他和许多程序员抱怨“Java太啰嗦”或“繁文缛节”太多,他提到:开发者想要创建普通的数据载体通常不得不编写大量低价值、重复且容易出错的代码.比如:constructor,getter/setter,equals(),hashCode(),andtoString()等,所以很多人选择使用IDE的功能来自动生成这些代码。也有一些开发者选择使用一些第三方库,比如Lombok等来生成这些方法,这会导致令人惊讶的行为和较差的可调试性。那么,BrianGoetz所说的纯数据载体是什么意思呢?他举了一个简单的例子:finalclassPoint{publicfinalintx;publicfinalinty;publicPoint(intx,inty){this.x=x;this.y=y;}//state-basedimplementationsofequals,hashCode,toString//nothingelse}Piont其实是一个纯数据载体,即一个“点”包含x坐标和y坐标,只提供构造函数,以及一些equals、hashCode等方法。因此,BrianGoetz想出了一个主意。他提到Java可以用另一种方式表达这种纯粹的数据载体。其实在其他的面向对象语言中,早就对这种纯数据载体有了单独的定义,比如Scala中的case,Kotlin中的data,C#中的record。这些定义虽然在语义上有所不同,但它们的共同点是类的部分或全部状态都可以直接在类头中描述,而这个类只包含纯数据。因此,他提出,是不是可以用下面的方式在Java中定义一个纯数据载体?recordPoint(intx,inty){}大神说要用record,于是就有了大神的吐槽,我们平时要写很多代码才能让class有用。比如下面的:toString()方法hashCode()和equals()方法Getter方法一个普通的构造器对于这么简单的类,这些方法通常很枯燥,重复,很容易机械生成之类的东西(IDE通常提供这种功能)。当您阅读其他人的代码时,它可能会更加混乱。比如有人可能会用IDE生成的hashCode()和equals()来处理class的所有字段,但是不检查每一行实现,他怎么能确定自己写的正确呢?如果重构过程中添加了字段没有重新生成方法会怎样?大神BrianGoetz提出了用record来定义纯数据载体的想法,于是Java14中包含了一个新特性:EP359:Records,作者是BrianGoetzRecords,以扩展Java语言语法为目标,Records提供了一种用于声明类的紧凑语法,用于创建“字段,只是字段,除了字段什么都不是”的类。通过在类上做这样的声明,编译器可以自动创建所有的方法,让所有的字段都参与到方法中,比如hashCode()。这是JDK14中的一个预览特性,反编译Records的用法比较简单,就像定义一个Java类:recordPerson(StringfirstName,StringlastName){}如上,我们定义了一个Person记录,它包含两个组成部分:firstName和lastName,和一个空的班级身体。所以,这东西好像是语法糖,他是怎么实现的呢?我们先尝试编译一下,记得使用--enable-preview参数,因为records功能在JDK14中还是预览功能。javac--enable-preview--release14Person.java注:Person.javausespreviewlanguagefeatures。-Xlint:预览细节。如前所述,Record只是一个类,其目的是保存和公开数据。我们再看看用javap反编译,我们会得到如下代码:;publicfinalbooleanequals(java.lang.Object);publicjava.lang.StringfirstName();publicjava.lang.StringlastName();}通过反编译得到的类,我们可以得到如下信息:1.生成了一个final类型的Person类(class),表示这个类不能再有子类。2、该类继承了java.lang.Record类。我们使用enum创建的枚举都默认继承了java.lang.Enum。3.类中有两个privatefinal类型的属性。因此,record定义的类中的属性应该是privatefinal类型。4.有一个public构造函数,入参是两个主要的属性。如果通过字节码查看它的方法体,它的内容就是下面这段代码,大家一定很熟悉:publicPerson(StringfirstName,StringlastName){this.firstName=firstName;this.lastName=lastName;}5.有两个getter方法,他们分别被称为firstName和lastName。这与JavaBean中定义的命名方式不同。可能大师是想告诉我们,record的定义不是这样定义的JavaBean。6、它还会自动为我们生成toString()、hashCode()和equals()方法。值得一提的是,这三个方法依赖于invokedynamic来动态调用包含隐式实现的适当方法。你也可以这样玩。在前面的示例中,我们只是创建了一条记录。那么,record中能不能有其他的成员变量和方法呢?让我们来看看。1.我们无法将实例字段添加到记录中。但是,我们可以添加静态字段。recordPerson(StringfirstName,StringlastName){staticintx;}2、我们可以定义静态方法和实例方法,可以操作对象的状态。recordPerson(StringfirstName,StringlastName){staticintx;publicstaticvoiddoX(){x++;}publicStringgetFullName(){returnfirstName+""+lastName;}}3.我们还可以添加构造函数。recordPerson(StringfirstName,StringlastName){staticintx;publicPerson{if(firstName==null){thrownewIllegalArgumentException("firstNamecannotbenull!");}}publicPerson(StringfullName){this(fullName.split("")[0],this(fullName.split("")[1])}}所以,我们可以给记录添加静态字段/方法,但问题是,我们应该吗?记住,引入记录背后的目标是让开发者能够分组相关字段一起作为一个单一的不可变数据项而不需要编写冗长的代码。这意味着每当你想向你的记录添加更多字段/方法时,考虑是否应该使用完整的类来替换它。摘要记录解决了使用的常见问题类作为数据包装器。纯数据类从几行代码显着简化为一行代码。但是,记录目前是一种预览语言功能,这意味着,虽然它已完全实现,但在JDK中并未标准化。那么问题来了,如果你使用Java14,你会直到使用龙目岛?哦不,你可能短时间内不会用到它,因为你可能对Java8还不熟悉~
