@Data
这个注解是最常用的,是一个类级别注解,同时也是一个 复合型注解
,所谓复合型注解,就是多个注解的集合体。
这个注解包含了 @Getter
、@Setter
、@ToString
、@EqualsAndHashCode
、@RequiredArgsConstructor
,每个注解的作用会在稍后一一介绍。
此注解有一个参数 staticConstructor
用来生成静态构造器,对应值为 构造器名。
使用注解的效果直接上代码:
源文件
1
2
3
4
5
6
7
8
9import lombok.Data;
public class DataExample {
private final String name;
private int age;
private double score;
private String[] tags;
}字节码文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54import java.beans.ConstructorProperties;
import java.util.Arrays;
public class DataExample {
private final String name;
private int age;
private double score;
private String[] tags;
private DataExample(String name) {this.name = name;}
public static DataExample of(String name) {return new DataExample(name);}
public String getName() {return this.name;}
public int getAge() {return this.age;}
public void setAge(int age) {this.age = age;}
public double getScore() {return this.score;}
public void setScore(double score) {this.score = score;}
public String[] getTags() {return this.tags;}
public void setTags(String[] tags) {this.tags = tags;}
protected boolean canEqual(Object other) {return other instanceof DataExample;}
public boolean equals(Object o) {
if(o == this) {return true;} else if(!(o instanceof DataExample)) {return false;} else {DataExample other = (DataExample)o;
if(!other.canEqual(this)) {return false;} else {Object this$name = this.getName();
Object other$name = other.getName();
if(this$name == null) {if(other$name == null) {return this.getAge() != other.getAge()?false:(Double.compare(this.getScore(), other.getScore())!= 0?false:Arrays.deepEquals(this.getTags(), other.getTags()));}
} else if(this$name.equals(other$name)) {return this.getAge() != other.getAge()?false:(Double.compare(this.getScore(), other.getScore())!= 0?false:Arrays.deepEquals(this.getTags(), other.getTags()));}
return false;
}
}
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $name = this.getName();
int result = result * 59 + ($name == null?43:$name.hashCode());
result = result * 59 + this.getAge();
long $score = Double.doubleToLongBits(this.getScore());
result = result * 59 + (int)($score >>> 32 ^ $score);
result = result * 59 + Arrays.deepHashCode(this.getTags());
return result;
}
public String toString() {
return "DataExample(name=" + this.getName() + ", age=" + this.getAge()+ ", score=" + this.getScore() + ", tags=" + Arrays.deepToString(this.getTags()) + ")";
}
}
通过官方文档,可以得知,当使用 @Data
注解时,则有了 @EqualsAndHashCode
注解,那么就会在此类中存在equals(Object other)
和 hashCode()
方法,且不会使用父类的属性,这就导致了可能的问题。
比如,有多个类有相同的部分属性,把它们定义到父类中,恰好id(数据库主键)也在父类中,那么就会存在部分对象在比较时,它们并不相等,却因为lombok自动生成的equals(Object other)
和 hashCode()
方法判定为相等,从而导致出错。
修复此问题的方法很简单:
1. 使用@Getter @Setter @ToString
代替@Data
并且自定义equals(Object other)
和 hashCode()
方法,比如有些类只需要判断主键id是否相等即足矣。
2. 或者使用在使用@Data
时同时加上@EqualsAndHashCode(callSuper=true)
注解。
@Getter & @Setter
这两个注解是生成 get、set 方法的,可以写在类上,也可以写在属性上。这两个注解有一个重要的属性 value
, 用来控制生成的方法的访问级别,对应值是 AccessLevel 类型枚举,分别有 PUBLIC, PROTECTED, PACKAGE, 和 PRIVATE,默认为 PUBLIC。
源文件
1
2
3
4public class Example {
private int age = 10;
private String name;
}字节码文件
1
2
3
4
5
6
7
8
9public class Example {
private int age = 10;
private String name;
public DataExample(){}
public int getAge() {return this.age;}
public void setAge(int age) {this.age = age;}
protected void setName(String name) {this.name = name;}
}
@ToString
覆盖默认 toString()
方法。其中常用参数介绍:
- includeFieldNames:生成的 toString() 方法是否包含字段名,布尔类型。
- exclude:不显示在 toString() 中的字段,String 数组。
- callSuper:调用父类 toString(),布尔类型。
源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24import lombok.ToString;
public class ToStringExample {
private static final int STATIC_VAR = 10;
private String name;
private Shape shape = new Square(5, 10);
private String[] tags;
private int id;
public String getName() {
return this.getName();
}
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
}
}字节码(部分)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24import java.util.Arrays;
public class ToStringExample {
private static final int STATIC_VAR = 10;
private String name;
private Shape shape = new Square(5, 10);
private String[] tags;
private int id;
public String getName() {return this.getName();}
public static class Square extends Shape {
private final int width, height;
public Square(int width, int height) {
this.width = width;
this.height = height;
}
public String toString() {return "Square(super=" + super.toString() + ", width=" + this.width + ", height=" + this.height + ")";}
}
public String toString() {return "ToStringExample(" + this.getName() + "," + this.shape + "," + Arrays.deepToString(this.tags) + ")";}
}
@EqualsAndHashCode
源文件
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Person extends SentientBeing {
enum Gender { Male, Female }
private String name;
private Gender gender;
private String ssn;
private String address;
private String city;
private String state;
private String zip;
}字节码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40public class Person extends SentientBeing {
enum Gender {
/*public static final*/ Male /* = new Gender() */,
/*public static final*/ Female /* = new Gender() */;
}
private String name;
private Gender gender;
private String ssn;
private String address;
private String city;
private String state;
private String zip;
.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;
}
.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;
}
}
生成 equals 和 hashCode 方法,callSuper 及 exclude 等参数同 @ToString
。
如果@EqualsAndHashCode不是想排除某些字段,而是只包含某些字段:
1 |
|
onlyExplicitlyIncluded默认为false,所有的非静态和非瞬态的字段都会被包含进equals和hashCode方法中;为true时,只有在字段上明确使用了EqualsAndHashCode.Include注解才会被包含进equals和hashCode方法中。
@RequiredArgsConstructor
作用同注解名,生成必须的构造器,即:无参构造器,若类中用 final
标记的字段,则生成的是包含所有 final 类型字段的构造器。
@NoArgsConstructor & @AllArgsConstructor
无参构造器及全参构造器。注解参数同 @RequiredArgsConstructor。
@NonNull
一般写在方法签名中,被此注解的字段,传入值禁止为 null
,否则抛 NPE(NullPointerException)
。
源文件
1
2
private List<Person> members;字节码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private List<Person> members;
public Family(final List<Person> members) {
if (members == null) throw new java.lang.NullPointerException("members");
this.members = members;
}
public List<Person> getMembers() {
return members;
}
public void setMembers(final List<Person> members) {
if (members == null) throw new java.lang.NullPointerException("members");
this.members = members;
}
@Cleanup
被此注解标记的对象,在使用完毕后自动调用对象 close()
方法。
源文件
1
InputStream in = new FileInputStream("some/file");
字节码
1
2
3
4
5
6InputStream in = new FileInputStream("some/file");
try {
// do something
} finally {
if (in != null) in.close();
}
@Log & @Slf4j & @CommonsLog & @JBossLog 等
注解在类上,在类中自动生成一个属性名为 log
的日志对象,无需再写类似如下代码:
1 | private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName()); |
@Builder
生成链式构造,可与 @Data 等注解共存。
1 | User user = User.builder() |
@Synchronized
源文件
1
2
3
4
5
6private DateFormat format = new SimpleDateFormat("MM-dd-YYYY");
public String synchronizedFormat(Date date) {
return format.format(date);
}字节码
1
2
3
4
5
6
7
8private 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);
}
}
@Accessors
在你的工作中,有时候可能会看到 @Accessors(chain = true) 这样的注解,他是lombok插件包中的一个注解,那么它是什么意思呢?
1. @Accessors 源码
我们打开 @Accessors 的源码可以看到:
(1)该注解主要作用是:当属性字段在生成 getter 和 setter 方法时,做一些相关的设置。
(2)当它可作用于类上时,修饰类中所有字段,当作用于具体字段时,只对该字段有效。
该字段共有三个属性,分别是 fluent,chain,prefix,下面我们分别来说明下,他的意思分别是什么?
2. @Accessors 属性说明
2.1 fluent 属性
不写默认为false,当该值为 true 时,对应字段的 getter 方法前面就没有 get,setter 方法就不会有 set。
2.2 chain 属性
不写默认为false,当该值为 true 时,对应字段的 setter 方法调用后,会返回当前对象。
2.3 prefix 属性
该属性是一个字符串数组,当该数组有值时,表示忽略字段中对应的前缀,生成对应的 getter 和 setter 方法。
比如现在有 xxName 字段和 yyAge 字段,xx 和 yy 分别是 name 字段和 age 字段的前缀。
那么,我们在生成的 getter 和 setter 方法如下,它也是带有 xx 和 yy 前缀的。
如果,我们把它的前缀加到 @Accessors 的属性值中,则可以像没有前缀那样,去调用字段的 getter和 setter 方法。
@SneakyThrows
自动抛受检异常,而无需显式在方法上使用throws语句
1 | import lombok.SneakyThrows; |
在日常开发中,@SneakyThrows用的并不多,因为只是将异常抛出throw,还是需要你在调用方法时对异常做处理,它只是一个简化try-catch的写法。说白了它啥没做什么实际工作,加上它只是为了能让原本使用try-catch或者throws的程序能够正常编译而不报错。
扩展阅读:常用开发库 - Lombok工具库详解