使用Lombok减少JavaBean的重复代码

简介: 使用Lombok减少JavaBean的重复代码

Lombok 介绍

  • Lombok 旨在通过用一组简单的注释来替代它们来减少代码的重复。
  • 例如,简单地将@Data注释添加到数据类中,如下所示,将在IDE中产生许多新方法:

图片.png

IntelliJ IDEA安装Lombok Plugin

  • 定位到 File > Settings > Plugins
  • 点击 Browse repositories
  • 搜索 Lombok Plugin
  • 点击 Install plugin
  • 重启 IDEA


maven依赖

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.16</version>
</dependency>


Lombok注释

@Getter和@Setter

  • @getter@setter注释分别为字段生成gettersetter
  • 生成的getter正确地遵循boolean 属性的约定,从而产生一个isfoo getter方法名,而不是任何布尔字段foogetfoo
  • 应该注意的是,如果注释字段所属的类包含与要生成的gettersetter同名的方法,则无论参数或返回类型如何,都不会生成相应的方法。
  • @getter@setter注释都采用一个可选的参数来指定生成方法的访问级别。使用这种方法可以克服这些缺点。
  • Lombok annotated code:(使用Lombok 注解的代码)
@Getter @Setter 
private boolean employed = true;
@Setter(AccessLevel.PROTECTED) 
private String name;
  • Equivalent Java source code:(编译后的代码)
private boolean employed = true;
private String name;
public boolean isEmployed() {
return employed;
}
public void setEmployed(final boolean employed) {
this.employed = employed;
}
protected void setName(final String name) {
this.name = name;
}


@NonNull

  • @NONULL注释用于表示对相应成员进行空检查的必要性。当放置在Lombok正在为其生成setter方法的字段时,将生成一个空检查,
  • 如果提供空值,将产生一个nullpointerException
  • 此外,如果Lombok正在为所属类生成构造函数,则该字段将被添加到签名构造函数中。
  • 这个注释反映了IntelliJ IDEAfindbug中的@NONULL@NONULL注解。Lombok对于主题上的这些变体是不可知的。如果Lombok遇到任何注释名称@notnull@non-null的成员,它将受到gener的尊重。
  • Lombok annotated code from the class Family:
@Getter @Setter @NonNull
private List<Person> members;
  • Equivalent Java source code:
@NonNull
private List<Person> members;
public Family(@NonNull final List<Person> members) {
    if (members == null) throw new java.lang.NullPointerException("members");
    this.members = members;
}
@NonNull
public List<Person> getMembers() {
    return members;
}
public void setMembers(@NonNull final List<Person> members) {
    if (members == null) throw new java.lang.NullPointerException("members");
    this.members = members;
}


@ToString

  1. 此注释生成tostring方法的实现。默认情况下,任何非静态字段都将包含在方法的名称-值对中。
  2. 如果需要,可以通过将注释参数包含字段名设置为false来抑制输出中的属性名称。
  3. 可以通过在排除参数中包括它们的字段名,从生成的方法的输出中排除特定字段。
  4. Of参数只能用于列出输出中所需的字段。
  5. 还可以通过将回调超级参数设置为true来包含超类tostring方法的输出。


  • Lombok annotated code:
@ToString(callSuper=true,exclude="someExcludedField")
public class Foo extends Bar {
    private boolean someBoolean = true;
    private String someStringField;
    private float someExcludedField;
}


  • Equivalent Java source code:
public class Foo extends Bar {
    private boolean someBoolean = true;
    private String someStringField;
    private float someExcludedField;
    @java.lang.Override
    public java.lang.String toString() {
        return "Foo(super=" + super.toString() +
            ", someBoolean=" + someBoolean +
            ", someStringField=" + someStringField + ")";
    }
}


@EqualsAndHashCode

  1. 这个类级注释将生成equals hashcode方法,因为这两个方法本质上是由hashcode契约连接在一起的。
  2. 默认情况下,类中任何非静态或瞬态的字段都将会生成这两种方法。
  3. @tostring一样,提供排除参数是为了防止生成的逻辑中包含字段。只考虑这些字段。
  4. 也像@tostring一样,这个注释有一个回调父级参数。
  5. 在考虑当前类中的字段之前,将它设置为true将等于通过调用超类中的等于来验证相等。
  6. 对于hashcode方法,它将导致将父类的哈希代码的结果合并到计算器中。

 

  • Lombok annotated code:
@EqualsAndHashCode(callSuper=true,exclude={"address","city","state","zip"})
public class Person extends SentientBeing {
    enum Gender { Male, Female }
    @NonNull private String name;
    @NonNull private Gender gender;
    private String ssn;
    private String address;
    private String city;
    private String state;
    private String zip;
}


  • Equivalent Java source code:
public class Person extends SentientBeing {
    enum Gender {
        /*public static final*/ Male /* = new Gender() */,
        /*public static final*/ Female /* = new Gender() */;
    }
    @NonNull
    private String name;
    @NonNull
    private Gender gender;
    private String ssn;
    private String address;
    private String city;
    private String state;
    private String zip;
    @java.lang.Override
    public boolean equals(final java.lang.Object o) {
        if (o == this) return true;
        if (o == null) return false;
        if (o.getClass() != this.getClass()) return false;
        if (!super.equals(o)) return false;
        final Person other = (Person)o;
        if (this.name == null ? other.name != null : !this.name.equals(other.name)) return false;
        if (this.gender == null ? other.gender != null : !this.gender.equals(other.gender)) return false;
        if (this.ssn == null ? other.ssn != null : !this.ssn.equals(other.ssn)) return false;
        return true;
    }
    @java.lang.Override
    public int hashCode() {
        final int 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

  • @ToString, @EqualsAndHashCode, 所有属性的@Getter, 所有non-final属性的@Setter@RequiredArgsConstructor的组合,通常情况下,我们使用这个注解就足够了。
  • 虽然@Data非常有用,但它不提供与其他Lombok注释相同的控制粒度。
  • 为了覆盖默认的方法生成行为,请使用其他Lombok注释之一注释该类,字段或方法,并指定必要的参数值以实现所需的效果。
  • @Data确实提供了可用于生成静态工厂方法的单个参数选项。
  • staticConstructor参数的值设置为 所需的方法名称将导致Lombok将生成的构造函数设置为私有的,并公开给定名称的静态工厂方法。
  • Lombok annotated code:
@Data(staticConstructor="of")
public class Company {
    private final Person founder;
    private String name;
    private List<Person> employees;
}


  • Equivalent Java source code:
public class Company {
    private final Person founder;
    private String name;
    private List<Person> employees;
    private Company(final Person founder) {
        this.founder = founder;
    }
    public static Company of(final Person founder) {
        return new Company(founder);
    }
    public Person getFounder() {
        return founder;
    }
    public String getName() {
        return name;
    }
    public void setName(final String name) {
        this.name = name;
    }
    public List<Person> getEmployees() {
        return employees;
    }
    public void setEmployees(final List<Person> employees) {
        this.employees = employees;
    }
    @java.lang.Override
    public boolean equals(final java.lang.Object o) {
        if (o == this) return true;
        if (o == null) return false;
        if (o.getClass() != this.getClass()) return false;
        final Company other = (Company)o;
        if (this.founder == null ? other.founder != null : !this.founder.equals(other.founder)) return false;
        if (this.name == null ? other.name != null : !this.name.equals(other.name)) return false;
        if (this.employees == null ? other.employees != null : !this.employees.equals(other.employees)) return false;
        return true;
    }
    @java.lang.Override
    public int hashCode() {
        final int 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.Override
    public java.lang.String toString() {
        return "Company(founder=" + founder + ", name=" + name + ", employees=" + employees + ")";
    }
}


@Cleanup

使用该注解能够自动释放io资源
  • @Cleanup注释可以用来保证分配的资源被释放。
  •  
  • 当一个局部变量被@Cleanup注释,任何后续代码都被包装在一个 try/finally块中,以保证在当前作用域的末尾调用清理方法。
  • 默认情况下,@Cleanup 假定清理方法被命名为“close”,就像输入和输出流一样。
  • 但是,@Cleanup注释的value参数,可以用来提供不同的方法名称。只有不带参数的清理方法才能用于此批注。
  •  
  • 使用@Cleanup注释时还需要注意一点。
  • 如果清理方法抛出异常,它将抢占抛出方法体内的任何异常。
  • 这可能导致问题的实际原因被掩埋,因此在选择使用Project Lombok的资源管理时应该考虑这个问题。
  • 此外,随着Java 7中的自动化资源管理技术的发展,这种特殊的注释可能会相对短暂。

 

  • Lombok annotated code:

 

  • Equivalent Java source code:
public void testCleanUp() {
    try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            baos.write(new byte[]{'Y', 'e', 's'});
            System.out.println(baos.toString());
        } finally {
            baos.close();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}


@Synchronized

  • 在方法上使用synchronized关键字可能导致不幸的结果,任何从事多线程软件工作的开发人员都可以证明这一点。在实例方法的情况下,同步关键字将锁定当前对象(This),或者锁定静态方法的类对象。
  • 这意味着开发人员控制之外的代码有可能锁定同一个对象,从而导致死锁。
  • 通常情况下,最好是显式地锁定一个单独的对象,该对象仅用于该目的,而不以允许非请求锁定的方式暴露。ProjectLombok为此提供了@Synchronous注释。
  • @synchronized注释实例方法将提示Lombok生成一个名为$lock的私有锁定字段,该方法将在执行之前锁定该字段。
  • 类似地,用同样的方式注释静态方法将生成一个名为$lock的私有静态对象,以便静态方法以相同的方式使用。可以通过向@synchronized注释的value参数提供字段名来指定不同的锁定对象。
  • 当提供字段名时,开发人员必须将属性定义为Lombok不会生成它。

 

  • Lombok annotated code:
private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");
@Synchronized
public String synchronizedFormat(Date date) {
    return format.format(date);
}

 

  • Equivalent Java source code:
private final java.lang.Object $lock = new java.lang.Object[0];
private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");
public String synchronizedFormat(Date date) {
    synchronized ($lock) {
        return format.format(date);
    }
}


@SneakyThrows

  • @SneakyThrows可能是Lombok项目批注中最受批评者,因为它是对检查异常的直接攻击。
  • 关于检查异常的使用方面存在很多分歧,有大量开发人员认为他们是一个失败的实验。
  • 这些开发者会喜欢@SneakyThrows。在选中/未选中的异常栏的另一边的那些开发人员很可能会将其视为隐藏潜在问题。
  • IllegalAccessException如果IllegalAccessException或者某个父类未在throws子句中列出,则 投掷通常会生成“未处理的异常”错误:

图片.png

 

  • 注释时@SneakyThrows,错误消失。

 

图片.png

  • 默认情况下,@SneakyThrows将允许在没有在throws子句中声明的情况下引发任何检查的异常。
  • 通过为注释Class<? extends Throwable>的value参数提供一个可抛类(())数组,可以将其限制为一组特定的异常 

 

 

  • Lombok annotated code:
@SneakyThrows
public void testSneakyThrows() {
    throw new IllegalAccessException();
}
  • Equivalent Java source code:
public void testSneakyThrows() {
    try {
        throw new IllegalAccessException();
    } catch (java.lang.Throwable $ex) {
        throw lombok.Lombok.sneakyThrow($ex);
    }
}


局限性

  • 一个重要的问题是无法检测父类的构造函数。这意味着,如果父类没有默认构造函数,那么任何子类都不能使用@Data注释,
  • 而不显式地编写构造函数来使用可用的父类构造函数。
  • 无法支持多种参数构造器的重载

 

参考来源:http://jnb.ociweb.com/jnb/jnbJan2010.html

参考来源: http://blog.csdn.net/v2sking/article/details/73431364#t7



目录
相关文章
|
16天前
|
Java
在 Java 中捕获和处理自定义异常的代码示例
本文提供了一个 Java 代码示例,展示了如何捕获和处理自定义异常。通过创建自定义异常类并使用 try-catch 语句,可以更灵活地处理程序中的错误情况。
|
1月前
|
XML 安全 Java
Java反射机制:解锁代码的无限可能
Java 反射(Reflection)是Java 的特征之一,它允许程序在运行时动态地访问和操作类的信息,包括类的属性、方法和构造函数。 反射机制能够使程序具备更大的灵活性和扩展性
41 5
Java反射机制:解锁代码的无限可能
|
2天前
|
安全 Java API
Java中的Lambda表达式:简化代码的现代魔法
在Java 8的发布中,Lambda表达式的引入无疑是一场编程范式的革命。它不仅让代码变得更加简洁,还使得函数式编程在Java中成为可能。本文将深入探讨Lambda表达式如何改变我们编写和维护Java代码的方式,以及它是如何提升我们编码效率的。
|
27天前
|
jenkins Java 测试技术
如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例详细说明
本文介绍了如何使用 Jenkins 自动发布 Java 代码,通过一个电商公司后端服务的实际案例,详细说明了从 Jenkins 安装配置到自动构建、测试和部署的全流程。文中还提供了一个 Jenkinsfile 示例,并分享了实践经验,强调了版本控制、自动化测试等关键点的重要性。
60 3
|
1月前
|
存储 安全 Java
系统安全架构的深度解析与实践:Java代码实现
【11月更文挑战第1天】系统安全架构是保护信息系统免受各种威胁和攻击的关键。作为系统架构师,设计一套完善的系统安全架构不仅需要对各种安全威胁有深入理解,还需要熟练掌握各种安全技术和工具。
92 10
|
28天前
|
分布式计算 Java MaxCompute
ODPS MR节点跑graph连通分量计算代码报错java heap space如何解决
任务启动命令:jar -resources odps-graph-connect-family-2.0-SNAPSHOT.jar -classpath ./odps-graph-connect-family-2.0-SNAPSHOT.jar ConnectFamily 若是设置参数该如何设置
|
26天前
|
Java
Java代码解释++i和i++的五个主要区别
本文介绍了前缀递增(++i)和后缀递增(i++)的区别。两者在独立语句中无差异,但在赋值表达式中,i++ 返回原值,++i 返回新值;在复杂表达式中计算顺序不同;在循环中虽结果相同但使用方式有别。最后通过 `Counter` 类模拟了两者的内部实现原理。
Java代码解释++i和i++的五个主要区别
|
2月前
|
搜索推荐 Java 数据库连接
Java|在 IDEA 里自动生成 MyBatis 模板代码
基于 MyBatis 开发的项目,新增数据库表以后,总是需要编写对应的 Entity、Mapper 和 Service 等等 Class 的代码,这些都是重复的工作,我们可以想一些办法来自动生成这些代码。
33 6
|
2月前
|
Java
通过Java代码解释成员变量(实例变量)和局部变量的区别
本文通过一个Java示例,详细解释了成员变量(实例变量)和局部变量的区别。成员变量属于类的一部分,每个对象有独立的副本;局部变量则在方法或代码块内部声明,作用范围仅限于此。示例代码展示了如何在类中声明和使用这两种变量。
|
2月前
|
存储 Java API
优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。
【10月更文挑战第19天】本文介绍了如何优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。内容包括Map的初始化、使用Stream API处理Map、利用merge方法、使用ComputeIfAbsent和ComputeIfPresent,以及Map的默认方法。这些技巧不仅提高了代码的可读性和维护性,还提升了开发效率。
69 3