Lombok项目是一个Java库,它会自动插入您的编辑器和构建工具中,从而为您的Java增光添彩。
永远不要再编写另一个getter或equals方法,一个带有注释的类将具有功能全面的生成器,自动执行日志记录变量等等。
简而言之,就是自动帮您生成setter和getter,toString、equals等方法。
maven仓库查看版本并在pom.xml中添加依赖
org.projectlombok
lombok
1.18.12
provided
@Getter
@Setter
private boolean employed = true;
@Setter(AccessLevel.PROTECTED)
private String name;
等价于原Java代码:
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;
}
3.2.2 @NonNull
@NonNull批注用于指示需要对相应成员进行快速失败的空检查。 当放置在Lombok为其生成setter方法的字段上时,将生成null检查,如果提供null值,则将导致NullPointerException。 此外,如果Lombok正在为所属类生成构造函数,则该字段将添加到构造函数签名中,并且空检查将包含在生成的构造函数代码中。
此批注反映了在IntelliJ IDEA和FindBugs等中找到的@NotNull和@NonNull批注。 对于主题的这些变化,Lombok与注解无关。 如果Lombok遇到任何带有名称@NotNull或@NonNull的任何注解的成员,它将通过生成适当的相应代码来兑现它。 Lombok项目的作者进一步评论说,如果将这种类型的注解添加到Java中,则Lombok版本将被删除。
注解代码:
@Getter
@Setter
@NonNull
private List members;
等价于原Java代码:
@NonNull
private List members;
public Family(@NonNull final List members) {
if (members == null) throw new java.lang.NullPointerException("members");
this.members = members;
}
@NonNull
public List getMembers() {
return members;
}
public void setMembers(@NonNull final List members) {
if (members == null) throw new java.lang.NullPointerException("members");
this.members = members;
}
3.2.3 @ToString
该注释生成toString方法的实现。 默认情况下,所有非静态字段都将以名称/值对的形式包含在方法的输出中。 如果需要,可以通过将注解参数includeFieldNames设置为false来抑制在输出中包含属性名称。
通过将特定字段的字段名称包含在exclude参数中,可以从生成的方法的输出中排除特定字段。 或者,可以使用of参数来仅列出输出中所需的那些字段。 通过将callSuper参数设置为true,还可以包含超类的toString方法的输出。
注解代码:
@ToString(callSuper=true,exclude="someExcludedField")
public class Foo extends Bar {
private boolean someBoolean = true;
private String someStringField;
private float someExcludedField;
}
等价于原Java代码:
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 + ")";
}
}
3.2.4 @EqualsAndHashCode
这个类级别的注释将使Lombok生成equals和hashCode方法,因为两者通过hashCode契约本质上联系在一起。 默认情况下,两种方法都将考虑类中任何非静态或瞬态的字段。 与@ToString非常相似,提供了exclude参数以防止将字段包含在生成的逻辑中。 也可以使用of参数来仅列出应考虑的那些字段。
就像@ToString一样,此注释也有一个callSuper参数。将其设置为true会导致equals通过在考虑当前类中的字段之前从超类调用equals来验证相等性。对于hashCode方法,它导致将超类的hashCode的结果并入哈希计算中。将callSuper设置为true时,请确保父类中的equals方法正确处理实例类型检查。如果父类检查该类是否具有特定类型,而不仅仅是两个对象的类相同,则可能导致不良结果。如果超类使用的是Lombok生成的equals方法,那么这不是问题。但是,其他实现可能无法正确处理此情况。还要注意,当类仅扩展Object时,无法将callSuper设置为true,因为这将导致实例相等性检查,从而使字段比较短路。这是由于生成的方法调用了Object的equals实现,如果正在比较的两个实例不是同一实例,则返回false。结果,在这种情况下,Lombok将生成编译时错误。
注解代码:
@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;
}
等价于原Java代码:
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;
}
}
3.2.5 @Data
@Data批注可能是Project Lombok工具集中最常用的批注。 它结合了@ ToString,@ EqualsAndHashCode,@ Getter和@Setter的功能。 本质上,在类上使用@Data等同于使用默认的@ToString和@EqualsAndHashCode注释类以及使用@Getter和@Setter注释每个字段。 用@Data注释类也会触发Lombok的构造函数生成。 这将添加一个公共构造函数,该构造函数将任何@NonNull或final字段用作参数。 这提供了普通Java对象(POJO)所需的一切。
尽管@Data非常有用,但它不能提供与其他Lombok注释相同的控制粒度。 为了覆盖默认的方法生成行为,请使用其他Lombok批注之一对类,字段或方法进行批注,并指定必要的参数值以实现所需的效果。
@Data确实提供了可用于生成静态工厂方法的单个参数选项。 将staticConstructor参数的值设置为所需的方法名称将使Lombok将生成的构造函数设为私有,并公开具有给定名称的静态工厂方法。
注解代码:
@Data(staticConstructor="of")
public class Company {
private final Person founder;
private String name;
private List employees;
}
等价于原Java代码:
public class Company {
private final Person founder;
private String name;
private List 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 getEmployees() {
return employees;
}
public void setEmployees(final List 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 + ")";
}
}
3.2.6 @Cleanup
@Cleanup批注可用于确保释放分配的资源。 当使用@Cleanup注释局部变量时,任何后续代码都将包装在try / finally块中,以确保在当前作用域的末尾调用cleanup方法。 默认情况下,@ Cleanup假定清除方法与输入和输出流一样被命名为“ close”。 但是,可以为注释的value参数提供不同的方法名称。 该注释只能使用不带参数的清除方法。
使用@Cleanup注释时,还需要注意一些注意事项。 如果cleanup方法引发异常,它将抢占方法主体中引发的所有异常。 这可能导致问题被掩埋的实际原因,在选择使用Project Lombok的资源管理时应予以考虑。 此外,随着Java 7中自动资源管理的兴起,这个特定的注释可能相对较短。
注解代码:
public void testCleanUp() {
try {
@Cleanup
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(new byte[] {'Y','e','s'});
System.out.println(baos.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
等价于原Java代码:
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();
}
}
3.2.6 @Synchronized
在方法上使用synchronized 关键字可能会导致不幸的后果,任何从事多线程软件开发的开发人员都可以证明。 如果是实例方法,则synchronized 关键字将锁定当前对象(此对象);对于静态方法,该关键字将锁定该类对象。 这意味着开发人员无法控制代码锁定同一对象,从而导致死锁。 通常建议改为显式地锁定在专用于该目的的单独对象上,并且不要以允许未经请求的锁定的方式公开。 为此,Project Lombok提供了@Synchronized批注。
用@Synchronized注释实例方法将提示Lombok生成一个名为$lock的私有锁定字段,该方法将在执行之前在该字段上锁定。 类似地,以相同的方式注释静态方法将生成一个名为$lock的私有静态对象,以供静态方法以相同方式使用。 可以通过为注释的value参数提供字段名称来指定其他锁定对象。 提供字段名称时,开发人员必须定义属性,因为Lombok不会生成该属性。
注解代码:
private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");
@Synchronized
public String synchronizedFormat(Date date) {
return format.format(date);
}
等价于原Java代码:
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);
}
}
3.2.8 @SneakyThrows
@SneakyThrows可能是批评者最多的Project Lombok批注,因为它是对已检查异常的直接攻击。 在使用检查异常方面存在很多分歧,许多开发人员认为这是一个失败的实验。 这些开发人员将喜欢@SneakyThrows。 处于已检查/未检查异常范围另一侧的那些开发人员最有可能将其视为隐藏潜在问题。
如果在throws子句中未列出IllegalAccessException或某些父类,则抛出IllegalAccessException通常会生成“未处理的异常”错误:
当使用@SneakyThrows注释时,错误消失了。
默认情况下,@ SneakyThrows将允许在不声明throws子句的情况下引发任何已检查的异常。 通过为注释的value参数提供可抛出的类(Class)数组,可以将其限制为特定的一组异常。
注解代码:
@SneakyThrows
public void testSneakyThrows() {
throw new IllegalAccessException();
}
等价于原Java代码:
public void testSneakyThrows() {
try {
throw new IllegalAccessException();
} catch (java.lang.Throwable $ex) {
throw lombok.Lombok.sneakyThrow($ex);
}
查看上面的代码和Lombok.sneakyThrow(Throwable)的签名,会使大多数人认为该异常已包装在RuntimeException中并重新抛出,但是事实并非如此。 scratchyThrow方法将永远不会正常返回,而是将提供的throwable完全不变。
Example Code -LombokExample.zip
Reducing Boilerplate Code with Project Lombok