使用Lombok减少JavaBean的重复代码
程序员文章站
2022-05-08 16:37:44
...
Lombok 介绍
Lombok 旨在通过用一组简单的注释来替代它们来减少代码的重复。
例如,简单地将@Data注释添加到数据类中,如下所示,将在IDE中产生许多新方法:
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注释分别为字段生成getter和setter。
生成的getter正确地遵循boolean 属性的约定,从而产生一个isfoo getter方法名,而不是任何布尔字段foo的getfoo。
-
应该注意的是,如果注释字段所属的类包含与要生成的getter或setter同名的方法,则无论参数或返回类型如何,
都不会生成相应的方法。 @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 IDEA和findbug中的@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
此注释生成tostring方法的实现。默认情况下,任何非静态字段都将包含在方法的名称-值对中。
如果需要,可以通过将注释参数包含字段名设置为false来抑制输出中的属性名称。
可以通过在排除参数中包括它们的字段名,从生成的方法的输出中排除特定字段。
Of参数只能用于列出输出中所需的字段。
还可以通过将回调超级参数设置为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
这个类级注释将生成equals 和hashcode方法,因为这两个方法本质上是由hashcode契约连接在一起的。
默认情况下,类中任何非静态或瞬态的字段都将会生成这两种方法。
与@tostring一样,提供排除参数是为了防止生成的逻辑中包含字段。只考虑这些字段。
也像@tostring一样,这个注释有一个回调父级参数。
在考虑当前类中的字段之前,将它设置为true将等于通过调用超类中的等于来验证相等。
对于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子句中列出,则 投掷通常会生成“未处理的异常”错误:
注释时@SneakyThrows,错误消失。
默认情况下,@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