文章目录
Lombok 快速入门
Lombok 简介
Lombok 是一种 Java 实用工具,可用来帮助开发人员消除 Java 的冗长,尤其是对于简单的 Java 对象(POJO)。它通过注释实现这一目的。通过在开发环境中实现 Lombok,开发人员可以节省构建诸如
hashCode()
和
equals()
、
getter / setter
这样的方法以及以往用来分类各种 accessor 和 mutator 的大量时间。
Lombok 安装
由于 Lombok 仅在编译阶段生成代码,所以使用 Lombok 注解的源代码,在 IDE 中会被高亮显示错误,针对这个问题可以通过安装 IDE 对应的插件来解决。具体的安装方式可以参考:新版idea可略过
使 IntelliJ IDEA 支持 Lombok 方式如下:
- Intellij 设置支持注解处理- 点击 File > Settings > Build > Annotation Processors- 勾选 Enable annotation processing
- 安装插件- 点击 Settings > Plugins > Browse repositories- 查找 Lombok Plugin 并进行安装- 重启 IntelliJ IDEA
- 将 lombok 添加到 pom 文件
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.8</version></dependency>
Lombok 使用
Lombok 提供注解 API 来修饰指定的类:
@Getter and @Setter
@Getter and @Setter Lombok 代码:
@Getter@Setterprivateboolean employed =true;@Setter(AccessLevel.PROTECTED)privateString name;
等价于 Java 源码:
privateboolean employed =true;privateString name;publicbooleanisEmployed(){return employed;}publicvoidsetEmployed(finalboolean employed){this.employed = employed;}protectedvoidsetName(finalString name){this.name = name;}
@NonNull
@NonNull Lombok 代码:
@Getter@Setter@NonNullprivateList<Person> members;
等价于 Java 源码:
@NonNullprivateList<Person> members;publicFamily(@NonNullfinalList<Person> members){if(members ==null)thrownewjava.lang.NullPointerException("members");this.members = members;}@NonNullpublicList<Person>getMembers(){return members;}publicvoidsetMembers(@NonNullfinalList<Person> members){if(members ==null)thrownewjava.lang.NullPointerException("members");this.members = members;}
@ToString
@ToString Lombok 代码:
@ToString(callSuper=true,exclude="someExcludedField")publicclassFooextendsBar{privateboolean someBoolean =true;privateString someStringField;privatefloat someExcludedField;}
等价于 Java 源码:
publicclassFooextendsBar{privateboolean someBoolean =true;privateString someStringField;privatefloat someExcludedField;@java.lang.Overridepublicjava.lang.StringtoString(){return"Foo(super="+super.toString()+", someBoolean="+ someBoolean +", someStringField="+ someStringField +")";}}
@EqualsAndHashCode
@EqualsAndHashCode Lombok 代码:
@EqualsAndHashCode(callSuper=true,exclude={"address","city","state","zip"})publicclassPersonextendsSentientBeing{enumGender{Male,Female}@NonNullprivateString name;@NonNullprivateGender gender;privateString ssn;privateString address;privateString city;privateString state;privateString zip;}
等价于 Java 源码:
publicclassPersonextendsSentientBeing{enumGender{/*public static final*/Male/* = new Gender() */,/*public static final*/Female/* = new Gender() */;}@NonNullprivateString name;@NonNullprivateGender gender;privateString ssn;privateString address;privateString city;privateString state;privateString zip;@java.lang.Overridepublicbooleanequals(finaljava.lang.Object o){if(o ==this)returntrue;if(o ==null)returnfalse;if(o.getClass()!=this.getClass())returnfalse;if(!super.equals(o))returnfalse;finalPerson other =(Person)o;if(this.name ==null? other.name !=null:!this.name.equals(other.name))returnfalse;if(this.gender ==null? other.gender !=null:!this.gender.equals(other.gender))returnfalse;if(this.ssn ==null? other.ssn !=null:!this.ssn.equals(other.ssn))returnfalse;returntrue;}@java.lang.OverridepublicinthashCode(){finalint PRIME =31;int result =1;
result = result * PRIME +super.hashCode();
result = result * PRIME +(this.name ==null?0:this.name.hashCode());
result = result * PRIME +(this.gender ==null?0:this.gender.hashCode());
result = result * PRIME +(this.ssn ==null?0:this.ssn.hashCode());return result;}}
@Data
@Data Lombok 代码:
@Data(staticConstructor="of")publicclassCompany{privatefinalPerson founder;privateString name;privateList<Person> employees;}
@Data :注解在类上;提供类所有属性的
get
和
set
方法,此外还提供了
equals
、
canEqual
、
hashCode
、
toString
方法。
等价于 Java 源码:
publicclassCompany{privatefinalPerson founder;privateString name;privateList<Person> employees;privateCompany(finalPerson founder){this.founder = founder;}publicstaticCompanyof(finalPerson founder){returnnewCompany(founder);}publicPersongetFounder(){return founder;}publicStringgetName(){return name;}publicvoidsetName(finalString name){this.name = name;}publicList<Person>getEmployees(){return employees;}publicvoidsetEmployees(finalList<Person> employees){this.employees = employees;}@java.lang.Overridepublicbooleanequals(finaljava.lang.Object o){if(o ==this)returntrue;if(o ==null)returnfalse;if(o.getClass()!=this.getClass())returnfalse;finalCompany other =(Company)o;if(this.founder ==null? other.founder !=null:!this.founder.equals(other.founder))returnfalse;if(this.name ==null? other.name !=null:!this.name.equals(other.name))returnfalse;if(this.employees ==null? other.employees !=null:!this.employees.equals(other.employees))returnfalse;returntrue;}@java.lang.OverridepublicinthashCode(){finalint PRIME =31;int result =1;
result = result * PRIME +(this.founder ==null?0:this.founder.hashCode());
result = result * PRIME +(this.name ==null?0:this.name.hashCode());
result = result * PRIME +(this.employees ==null?0:this.employees.hashCode());return result;}@java.lang.Overridepublicjava.lang.StringtoString(){return"Company(founder="+ founder +", name="+ name +", employees="+ employees +")";}}
@Cleanup
@Cleanup Lombok 代码:
publicvoidtestCleanUp(){try{@CleanupByteArrayOutputStream baos =newByteArrayOutputStream();
baos.write(newbyte[]{'Y','e','s'});System.out.println(baos.toString());}catch(IOException e){
e.printStackTrace();}}
@Cleanup
:作用于变量,自动关闭资源,仅针对实现了
java.io.Closeable
接口的对象有效。
等价于 Java 源码:
publicvoidtestCleanUp(){try{ByteArrayOutputStream baos =newByteArrayOutputStream();try{
baos.write(newbyte[]{'Y','e','s'});System.out.println(baos.toString());}finally{
baos.close();}}catch(IOException e){
e.printStackTrace();}}
@Synchronized
@Synchronized Lombok 代码:
privateDateFormat format =newSimpleDateFormat("MM-dd-YYYY");@SynchronizedpublicStringsynchronizedFormat(Date date){return format.format(date);}
@Synchronized
:作用于方法,可以替换 synchronized 关键字或 lock 锁。
等价于 Java 源码:
privatefinaljava.lang.Object $lock =newjava.lang.Object[0];privateDateFormat format =newSimpleDateFormat("MM-dd-YYYY");publicStringsynchronizedFormat(Date date){synchronized($lock){return format.format(date);}}
@SneakyThrows
@SneakyThrows Lombok 代码:
@SneakyThrowspublicvoidtestSneakyThrows(){thrownewIllegalAccessException();}
@SneakyThrows
:作用于方法,对异常进行捕捉并抛出。
等价于 Java 源码:
publicvoidtestSneakyThrows(){try{thrownewIllegalAccessException();}catch(java.lang.Throwable $ex){throwlombok.Lombok.sneakyThrow($ex);}}
Lombok 使用注意点
谨慎使用
@Builder
在类上标注了
@Data
和
@Builder
注解的时候,编译时,lombok 优化后的 Class 中会没有默认的构造方法。在反序列化的时候,没有默认构造方法就可能会报错。
【示例】使用
@Builder
不当导致 json 反序列化失败
@Data@BuilderpublicclassBuilderDemo01{privateString name;publicstaticvoidmain(String[] args)throwsJsonProcessingException{BuilderDemo01 demo01 =BuilderDemo01.builder().name("demo01").build();ObjectMapper mapper =newObjectMapper();String json = mapper.writeValueAsString(demo01);BuilderDemo01 expectDemo01 = mapper.readValue(json,BuilderDemo01.class);System.out.println(expectDemo01.toString());}}
运行时会抛出异常:
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `io.github.dunwu.javatech.bean.lombok.BuilderDemo01` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)
at [Source: (String)"{"name":"demo01"}"; line: 1, column: 2]
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1432)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1062)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1297)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4218)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3214)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3182)
at io.github.dunwu.javatech.bean.lombok.BuilderDemo01.main(BuilderDemo01.java:22)
【示例】使用
@Builder
正确方法
@Data@Builder@NoArgsConstructor@AllArgsConstructorpublicclassBuilderDemo02{privateString name;publicstaticvoidmain(String[] args)throwsJsonProcessingException{BuilderDemo02 demo02 =BuilderDemo02.builder().name("demo01").build();ObjectMapper mapper =newObjectMapper();String json = mapper.writeValueAsString(demo02);BuilderDemo02 expectDemo02 = mapper.readValue(json,BuilderDemo02.class);System.out.println(expectDemo02.toString());}}
@Data
注解和继承
使用
@Data
注解时,则有了
@EqualsAndHashCode
注解,那么就会在此类中存在
equals(Object other)
和
hashCode()
方法,且不会使用父类的属性,这就导致了可能的问题。比如,有多个类有相同的部分属性,把它们定义到父类中,恰好 id(数据库主键)也在父类中,那么就会存在部分对象在比较时,它们并不相等,这是因为:lombok 自动生成的
equals(Object other)
和
hashCode()
方法判定为相等,从而导致和预期不符。
修复此问题的方法很简单:
- 使用
@Data
时,加上@EqualsAndHashCode(callSuper=true)
注解。 - 使用
@Getter @Setter @ToString
代替@Data
并且自定义equals(Object other)
和hashCode()
方法。
【示例】测试
@Data
和
@EqualsAndHashCode
@Data@ToString(exclude ="age")@EqualsAndHashCode(exclude ={"age","sex"})publicclassPerson{protectedString name;protectedInteger age;protectedString sex;}@Data@EqualsAndHashCode(callSuper =true, exclude ={"address","city","state","zip"})publicclassEqualsAndHashCodeDemoextendsPerson{@NonNullprivateString name;@NonNullprivateGender gender;privateString ssn;privateString address;privateString city;privateString state;privateString zip;publicEqualsAndHashCodeDemo(@NonNullString name,@NonNullGender gender){this.name = name;this.gender = gender;}publicEqualsAndHashCodeDemo(@NonNullString name,@NonNullGender gender,String ssn,String address,String city,String state,String zip){this.name = name;this.gender = gender;this.ssn = ssn;this.address = address;this.city = city;this.state = state;this.zip = zip;}publicenumGender{Male,Female}}@Test@DisplayName("测试 @EqualsAndHashCode")publicvoidtestEqualsAndHashCodeDemo(){EqualsAndHashCodeDemo demo1 =newEqualsAndHashCodeDemo("name1",EqualsAndHashCodeDemo.Gender.Female,"ssn","xxx","xxx","xxx","xxx");EqualsAndHashCodeDemo demo2 =newEqualsAndHashCodeDemo("name1",EqualsAndHashCodeDemo.Gender.Female,"ssn","ooo","ooo","ooo","ooo");Assertions.assertEquals(demo1, demo2);Person person =newPerson();
person.setName("张三");
person.setAge(20);
person.setSex("男");Person person2 =newPerson();
person2.setName("张三");
person2.setAge(18);
person2.setSex("男");Person person3 =newPerson();
person3.setName("李四");
person3.setAge(20);
person3.setSex("男");Assertions.assertEquals(person2, person);Assertions.assertNotEquals(person3, person);}
上面的单元测试可以通过,但如果将
@EqualsAndHashCode(callSuper = true, exclude = { "address", "city", "state", "zip" })
注掉就会报错。
版权归原作者 鱼找水需要时间 所有, 如有侵权,请联系我们删除。