1,场景
编译源代码时自动生成 get/set/hashCode/equals/toString 等方法。
2,原理
Lombok这款插件正是依靠可插件化的Java自定义注解处理API(JSR 269: Pluggable Annotation Processing API)来实现在Javac编译阶段利用“Annotation Processor”对自定义的注解进行预处理后生成真正在JVM上面执行的“Class文件”。
3,使用
- idea安装插件
- 导入依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.8</version>
</dependency>
4,常用注解
注解 | 场景说明 | 备注 |
---|---|---|
@Data | 包含注解的集合@ToString,@EqualsAndHashCode,所有字段的@Getter和所有非final字段的@Setter, @RequiredArgsConstructor。其示例代码可以参考上面几个注解的组合。 | |
@NonNull | 为方法或构造函数的参数提供非空检查 | |
@Cleanup | 自动释放资源 | |
Getter/@Setter | 针对类的属性字段自动生成Get/Set方法。 | 设置在属性字段之前 |
@NoArgsConstructor | 生成无参构造器 | |
@RequiredArgsConstructor | 生成指定参数构造器 | @RequiredArgsConstructor(staticName = "of") |
@AllArgsConstructor | 生成所有参数构造器 | @AllArgsConstructor(access = AccessLevel.PROTECTED) |
@ToString | 为使用该注解的类生成一个toString方法 | 默认的toString格式为:ClassName(fieldName= fieleValue ,fieldName1=fieleValue) 。 |
@EqualsAndHashCode | 为使用该注解的类自动生成equals和hashCode方法 | |
@Builder | 提供构建值对象的方式 | 不可处理父类属性 |
@Synchronized | 类似Java中的Synchronized 关键字,但是可以隐藏同步锁 | |
@Accessors(chain = true) | 开启链式调用 |
1)val
将变量申明是final类型。
public static void main(String[] args) {
val setVar = new HashSet<String>();
val listsVar = new ArrayList<String>();
val mapVar = new HashMap<String, String>();
//=>上面代码相当于如下:
final Set<String> setVar2 = new HashSet<>();
final List<String> listsVar2 = new ArrayList<>();
final Map<String, String> maps2 = new HashMap<>();
}
2)@NonNull
为方法或构造函数的参数提供非空检查。
public void notNullExample(@NonNull String string) {
//方法内的代码
}
//=>上面代码相当于如下:
public void notNullExample(String string) {
if (string != null) {
//方法内的代码相当于如下:
} else {
throw new NullPointerException("null");
}
}
3)@Cleanup
自动释放资源。
public void jedisExample(String[] args) {
try {
@Cleanup Jedis jedis = redisService.getJedis();
} catch (Exception ex) {
logger.error(“Jedis异常:”,ex)
}
//=>上面代码相当于如下:
Jedis jedis= null;
try {
jedis = redisService.getJedis();
} catch (Exception e) {
logger.error(“Jedis异常:”,ex)
} finally {
if (jedis != null) {
try {
jedis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
4)@ToString
为使用该注解的类生成一个toString方法,默认的toString格式为:ClassName(fieldName= fieleValue ,fieldName1=fieleValue)。
@ToString(callSuper=true,exclude="someExcludedField")
public class Demo extends Bar {
private boolean someBoolean = true;
private String someStringField;
private float someExcludedField;
}
//上面代码相当于如下:
public class Demo extends Bar {
private boolean someBoolean = true;
private String someStringField;
private float someExcludedField;
@ Override
public String toString() {
return "Foo(super=" + super.toString() +
", someBoolean=" + someBoolean +
", someStringField=" + someStringField + ")";
}
}
5)@EqualsAndHashCode
为使用该注解的类自动生成equals和hashCode方法。
@EqualsAndHashCode(exclude = {"id"}, callSuper =true)
public class LombokDemo extends Demo{
private int id;
private String name;
private String gender;
}
//上面代码相当于如下:
public class LombokDemo extends Demo{
private int id;
private String name;
private String gender;
@Override
public boolean equals(final 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 LombokDemo other = (LombokDemo)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;
return true;
}
@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());
return result;
}
}
默认情况下会使用非静态和非transient型字段来生成,但是你也通过在字段上添加 @EqualsAndHashCode.Include 或者@EqualsAndHashCode.Exclude 修改你使用的字段(甚至指定各种方法的输出)。或者你也可以通过在类上使用 @EqualsAndHashCode(onlyExplicitlyIncluded = true) ,且在特定字段或特定方法上添加 @EqualsAndHashCode.Include 来指定他们。
如果将@EqualsAndHashCode添加到继承于另一个类的类上,这个功能会有点棘手。一般情况下,为这样的类自动生成equals和hashCode方法是一个坏思路,因为超类也有定义了一些字段,他们也需要equals/hashCode方法但是不会自动生成。通过设置callSuper=true,可以在生成的equals和hashCode方法里包含超类的方法。对于hashCode,·super.hashCode()·会被包含在hash算法内,而对于equals,如果超类实现认为它与传入的对象不一致则会返回false。注意:并非所有的equals都能正确的处理这样的情况。然而刚好lombok可以,若超类也使用lombok来生成equals方法,那么你可以安全的使用它的equals方法。如果你有一个明确的超类, 你得在callSuper上提供一些值来表示你已经斟酌过,要不然的话就会产生一条警告信息。
当你的类没有继承至任何类(非java.lang.Object, 当然任何类都是继承于Object类的),而你却将callSuer置为true, 这会产生编译错误(译者注: java: Generating equals/hashCode with a supercall to java.lang.Object is pointless. )。因为这会使得生成的equals和hashCode方法实现只是简单的继承至Object类的方法,只有相同的对象并且相同的hashCode才会判定他们相等。若你的类继承至另一个类又没有设置callSuper, 则会产品一个告警,因为除非超类没有(或者没有跟相等相关的)字段,否则lombok无法为你生成考虑超类声明字段的实现。
6)@Builder
提供构建值对象的方式。
@Builder
@AllArgsConstructor //联合使用
public class BuilderExample {
private String name;
private int age;
}
BuilderExample example = BuilderExample.builder()
.name("aaa")
.age(1)
.build();
//上面代码相当于如下:
public class BuilderExample {
private String name;
private int age;
BuilderExample(String name, int age) {
this.name = name;
this.age = age;
}
public static BuilderExampleBuilder builder() {
return new BuilderExampleBuilder();
}
public static class BuilderExampleBuilder {
private String name;
private int age;
BuilderExampleBuilder() {
}
public BuilderExampleBuilder name(String name) {
this.name = name;
return this;
}
public BuilderExampleBuilder age(int age) {
this.age = age;
return this;
}
public BuilderExample build() {
Set<String> occupations = new HashSet<>();
return new BuilderExample(name, age, occupations);
}
@verride
public String toString() {
return "BuilderExample.BuilderExampleBuilder(name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";
}
}
}
7)@Synchronized
类似Java中的Synchronized 关键字,但是可以隐藏同步锁。
@Synchronized
public static void hello() {
System.out.println("world");
}