0


Java使用Lombok详解

文章目录

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" })

注掉就会报错。


本文转载自: https://blog.csdn.net/weixin_43847283/article/details/129802157
版权归原作者 鱼找水需要时间 所有, 如有侵权,请联系我们删除。

“Java使用Lombok详解”的评论:

还没有评论