翻译|徐蕾评论|梁策孙淑娟长期以来,Java被一些开发者诟病冗长。即使是最热情的Java开发人员也可能不得不承认,在Java中声明一个只有两个属性的bean类感觉有点荒谬。因为如果按照推荐的规范,不仅会增加getter和setter方法,还会增加toString、hashcode和equals方法的重写。最终,一大块样板代码将迫使开发人员放弃Java语言。Javaimportjava.util.Objects;publicclassCar{privateStringbrand;privateStringmodel;privateintyear;publicStringgetBrand(){returnbrand;}publicvoidsetBrand(Stringbrand){this.brand=brand;}publicStringgetModel(){returnmodel;}publicvoidsetModel(Stringmodel){this.model=model;}publicintgetYear(){returnyear;}publicvoidsetYear(intyear){this.year=year;}@OverridepublicStringtoString(){return"Car{"+"brand='"+brand+'\''+",model='"+model+'\''+",year="+year+'}';}@Overridepublicbooleanequals(Objecto){if(this==o)returntrue;if(o==null||getClass()!=o.getClass())returnfalse;Carcar=(Car)o;returnyear==car.year&&Objects.equals(brand,car.brand)&&Objects.equals(model,car.model);}@OverridepublicinthashCode(){返回Objects.hash(brand,model,year);}}幸运的是,龙目岛的横空出世大大减轻了Java开发者的痛苦。但是既然有了类似功能的JavaRecord类型,有人可能会问:Record能不能完全替代Lombok?1.什么是龙目岛?Lombok是一个与开发环境高度集成的Java类库(当然也可以看作是一种语法糖),通过注解改进(spice)代码,在Java社区中被广泛接受和使用。使用Lombok后,我们新建一个名为Car的类,如下:Javaimportlombok.Data;@DatapublicclassCar{privateStringbrand;privateStringmodel;privateintyear;}代码更加简洁,不会影响之前的版本任何功能。2.什么是JavaRecord?Java定义的每一个Record类型都可以简单的看成是值对象(ValueObject)模式的实现。它本质上是一个Java类,其中所有属性都是最终的。所以在创建对象的时候需要传递所有的类属性。JavaRecord是在Java14中引入的,它将继续改进和改进类设计。像这样通过Record创建一个新的Car类:JavapublicrecordCar(Stringbrand,Stringmodel,intyear){与之前的版本相比,改进非常明显。下面将对Lombok的一些特点进行分析,并与Record进行对比,以评价Lombok能否永久退出历史舞台。3.不可变性Record默认是不可变的,也就是说所有的类属性都隐式声明为final。我们通常认为Records类似于ValueObjects,但是它们没有setter方法,所有的值都需要在构造函数中传递。Lombok可以使用@Value注解来实现相同的效果,但也可以使用@Data注解来保持可变性。javaimportlombok.Value;@ValuepublicclassCar{privateStringbrand;privateStringmodel;privateintyear;}4.Bean契约Record不打算遵循bean契约,获取对象的方法没有使用getX方法来name,anddoesnot然后提供一个setter方法和一个无参数的构造函数。另一方面,Lombok只需使用@Data注释即可轻松地将类转换为JavaBean。5.Builder建造者模式是一种改进对象并创建出色语法的设计模式。Lombok为我们提供了@Builder这个非常实用的注解,可以帮助我们实现所有的样板代码。截至目前,JavaRecord不打算提供这样的实现。Javaimportlombok.Builder;@BuilderpublicclassCar{privateStringbrand;privateStringmodel;privateintyear;publicstaticvoidmain(String[]args){CarmyCamaro=Car.builder().brand("Chevrolet").model("Camaro").year(2022).build();}}6.多字段类Record只对少数字段友好。但是如果再给它加上10个字段,就会得到一个庞大的构造函数(输入参数很多),随之而来的是多参数构造函数带来的固有问题(传参容易错位,方法重载难以判断等).).JavapublicrecordDetailedCar(Stringbrand,Stringmodel,intyear,StringengineCode,StringengineType,StringrequiredFuel,StringfuelSystem,StringmaxHorsePower,StringmaxTorque,floatfuelCapacity){}JavaDetailedCarcamaroDetailed=newDetailedCar("Chevrolet","Camaro",2022,"LTG","Turbocharged","GasI4","DirectInjection","275@560","295@3000-4500",19.0f);使用Lombok,我们可以决定创建一个bean类是选择使用setter来设置对象的状态,还是使用更简洁的方式使用构建器构造实例。唯一需要注意的是,由于它不会强制默认设置所有属性,因此它可能会使实例处于不完整状态。当然@Builder注解支持我们将类中的所有属性都标记为@nonNull,这样在构造时就需要属性了。如果缺少必需的属性,则会在运行时抛出异常,而不是在编译时强制抛出异常。Javaimportlombok.Builder;importlombok.NonNull;@BuilderpublicclassDetailedCar{@NonNullprivateStringbrand;@NonNullprivateStringmodel;@NonNullprivateintyear;@NonNullprivateStringengineCode;@NonNullprivateStringengineType;@NonNullprivateStringNullprivateStringrequiredStringonNuFuel;@NonNullprivateStringmaxHorsePower;@NonNullprivateStringmaxTorque;@NonNullprivatefloatfuelCapacity;publicstaticvoidmain(String[]args){DetailedCarcamaroIncomplete=DetailedCar.builder().brand("Chevrolet").model("Camaro").year(2022).build();}}Output:Exceptioninthread"main"java.lang.NullPointerException:engineCodeismarkednon-nullbutisnull7.继承目前为止,JavaRecord类不支持继承,所以它不能通过扩展其他Record类来创建新的Record类可能是模型设计的限制。尽管如此,我们也必须认识到组合优于继承(面向对象设计原则七)。Java@Data@ToString(callSuper=true)@EqualsAndHashCode(callSuper=true)publicclassCarextendsMotorVehicle{privateStringbrand;privateStringmodel;privateintyear;}8.结语Record是Java的一个优秀的新特性,它正在推动代码更简洁,所以应该经常使用。对于提供了很多功能的Lombok,考虑到Java变化缓慢,在项目中完全取代它似乎还为时过早。原文链接:https://dzone.com/articles/records-vs-lombok译者介绍徐雷,社区编辑,某知名电商技术副总监,专注于Java后端开发、技术管理、架构优化、分布式开发等领域。
