Lombok中好用的注解
@EqualsAndHashCode
是否在子类的equals和hashCode中加入父类的属性判断。参数callSuper默认为false(即不对父类的属性进行判断)。
如果不加的话在进行子类队形判断时不会对父类的对象进行判断,这回导致比较的结果不正确。
Bean 中的链式风格
@Accessors
作用
形成链式操作。
参数
- chain:为 true 时,setter 链式返回,即 setter 的返回值为 this
- fluent:为 true 时,默认设置 chain 为 true,setter 的方法名修改为字段名
什么是链式风格?我来举个例子,看下面这个 Student 的 bean:
public class Student {
private String name;
private int age;
public String getName() {
return name;
}
public Student setName(String name) {
this.name = name;
return this;
}
public int getAge() {
return age;
}
public Student setAge(int age) {
return this;
}
}
仔细看一下 set 方法,这样的设置便是 chain 的 style,调用的时候,可以这样使用:
Student student = new Student()
.setAge(24)
.setName("zs");
相信合理使用这样的链式代码,会更多的程序带来很好的可读性,那看一下如果使用 lombok 进行改善呢,请使用 @Accessors(chain = true),看如下代码:
@Accessors(chain = true)
@Setter
@Getter
public class Student {
private String name;
private int age;
}
这样就完成了一个对于 bean 来讲很友好的链式操作。
静态构造方法
静态构造方法的语义和简化程度真的高于直接去 new 一个对象。比如 new 一个 List 对象,过去的使用是这样的:
List<String> list = new ArrayList<>();
看一下 guava 中的创建方式:
List<String> list = Lists.newArrayList();
Lists 命名是一种约定(俗话说:约定优于配置),它是指 Lists 是 List 这个类的一个工具类,那么使用 List 的工具类去产生 List,这样的语义是不是要比直接 new 一个子类来的更直接一些呢,答案是肯定的,再比如如果有一个工具类叫做 Maps,那你是否想到了创建 Map 的方法呢:
HashMap<String, String> objectObjectHashMap = Maps.newHashMap();
好了,如果你理解了我说的语义,那么,你已经向成为 Java 程序员更近了一步了。
再回过头来看刚刚的 Student,很多时候,我们去写 Student 这个 bean 的时候,他会有一些必输字段,比如 Student 中的 name 字段,一般处理的方式是将 name 字段包装成一个构造方法,只有传入 name 这样的构造方法,才能创建一个 Student 对象。
接上上边的静态构造方法和必传参数的构造方法,使用 lombok 将更改成如下写法(@RequiredArgsConstructor 和 @NonNull):
@Accessors(chain = true)
@Setter
@Getter
@RequiredArgsConstructor(staticName = "ofName")
public class Student {
@NonNull private String name;
private int age;
}
测试代码:
Student student = Student.ofName("zs");
这样构建出的 bean 语义是否要比直接 new 一个含参的构造方法(包含 name 的构造方法)要好很多。
当然,看过很多源码以后,我想相信将静态构造方法 ofName 换成 of 会先的更加简洁:
@Accessors(chain = true)
@Setter
@Getter
@RequiredArgsConstructor(staticName = "of")
public class Student {
@NonNull private String name;
private int age;
}
测试代码:
Student student = Student.of("zs");
当然他仍然是支持链式调用的:
Student student = Student.of("zs").setAge(24);
这样来写代码,真的很简洁,并且可读性很强。
使用 Builder
@Builder
作用
生成构建者(Builder)模式
Builder 模式我不想再多解释了,读者可以看一下《Head First》(设计模式) 的建造者模式。
今天其实要说的是一种变种的 builder 模式,那就是构建 bean 的 builder 模式,其实主要的思想是带着大家一起看一下 lombok 给我们带来了什么。
看一下 Student 这个类的原始 builder 状态:
public class Student {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public static Builder builder(){
return new Builder();
}
public static class Builder{
private String name;
private int age;
public Builder name(String name){
this.name = name;
return this;
}
public Builder age(int age){
this.age = age;
return this;
}
public Student build(){
Student student = new Student();
student.setAge(age);
student.setName(name);
return student;
}
}
}
调用方式:
Student student = Student.builder().name("zs").age(24).build();
这样的 builder 代码,让我是在恶心难受,于是我打算用 lombok 重构这段代码:
@Builder
public class Student {
private String name;
private int age;
}
调用方式:
Student student = Student.builder().name("zs").age(24).build();
@Singular
作用
结合@Builder注解使用,给到List字段添加一个方法,允许每次只添加一个元素,而不必添加一个列表。
例子
原始代码:
@Data
@Builder
public class Example {
@Singular("addUserName")
private List<String> userNameList;
public static void main(String[] args) {
Example demo = Example.builder().addUserName("demo").build();
System.out.println(demo);
}
}
生成的Class文件解析后的代码:
注意里面的
ExampleBuilder内部类中的addUserName方法
public class Example {
private List<String> userNameList;
public static void main(String[] args) {
Example demo = builder().addUserName("demo").build();
System.out.println(demo);
}
Example(final List<String> userNameList) {
this.userNameList = userNameList;
}
public static Example.ExampleBuilder builder() {
return new Example.ExampleBuilder();
}
public List<String> getUserNameList() {
return this.userNameList;
}
public void setUserNameList(final List<String> userNameList) {
this.userNameList = userNameList;
}
public boolean equals(final Object o) {
if (o == this) {
return true;
} else if (!(o instanceof Example)) {
return false;
} else {
Example other = (Example)o;
if (!other.canEqual(this)) {
return false;
} else {
Object this$userNameList = this.getUserNameList();
Object other$userNameList = other.getUserNameList();
if (this$userNameList == null) {
if (other$userNameList != null) {
return false;
}
} else if (!this$userNameList.equals(other$userNameList)) {
return false;
}
return true;
}
}
}
protected boolean canEqual(final Object other) {
return other instanceof Example;
}
public int hashCode() {
int PRIME = true;
int result = 1;
Object $userNameList = this.getUserNameList();
int result = result * 59 + ($userNameList == null ? 43 : $userNameList.hashCode());
return result;
}
public String toString() {
return "Example(userNameList=" + this.getUserNameList() + ")";
}
public static class ExampleBuilder {
private ArrayList<String> userNameList;
ExampleBuilder() {
}
public Example.ExampleBuilder addUserName(final String addUserName) {
if (this.userNameList == null) {
this.userNameList = new ArrayList();
}
this.userNameList.add(addUserName);
return this;
}
public Example.ExampleBuilder userNameList(final Collection<? extends String> userNameList) {
if (userNameList == null) {
throw new NullPointerException("userNameList cannot be null");
} else {
if (this.userNameList == null) {
this.userNameList = new ArrayList();
}
this.userNameList.addAll(userNameList);
return this;
}
}
public Example.ExampleBuilder clearUserNameList() {
if (this.userNameList != null) {
this.userNameList.clear();
}
return this;
}
public Example build() {
List userNameList;
switch(this.userNameList == null ? 0 : this.userNameList.size()) {
case 0:
userNameList = Collections.emptyList();
break;
case 1:
userNameList = Collections.singletonList((String)this.userNameList.get(0));
break;
default:
userNameList = Collections.unmodifiableList(new ArrayList(this.userNameList));
}
return new Example(userNameList);
}
public String toString() {
return "Example.ExampleBuilder(userNameList=" + this.userNameList + ")";
}
}
}
代理模式
@Delegate
作用
代理模式,把字段的方法代理给类,默认代理所有方法
参数
- types:指定代理的方法
- excludes:和 types 相反
正如我们所知的,在程序中调用 rest 接口是一个常见的行为动作,如果你和我一样使用过 spring 的 RestTemplate,我相信你会我和一样,对他抛出的非 http 状态码异常深恶痛绝。
所以我们考虑将 RestTemplate 最为底层包装器进行包装器模式的设计:
public abstract class FilterRestTemplate implements RestOperations {
protected volatile RestTemplate restTemplate;
protected FilterRestTemplate(RestTemplate restTemplate){
this.restTemplate = restTemplate;
}
//实现RestOperations所有的接口
}
然后再由扩展类对 FilterRestTemplate 进行包装扩展:
public class ExtractRestTemplate extends FilterRestTemplate {
private RestTemplate restTemplate;
public ExtractRestTemplate(RestTemplate restTemplate) {
super(restTemplate);
this.restTemplate = restTemplate;
}
public <T> RestResponseDTO<T> postForEntityWithNoException(String url, Object request, Class<T> responseType, Object... uriVariables)
throws RestClientException {
RestResponseDTO<T> restResponseDTO = new RestResponseDTO<T>();
ResponseEntity<T> tResponseEntity;
try {
tResponseEntity = restTemplate.postForEntity(url, request, responseType, uriVariables);
restResponseDTO.setData(tResponseEntity.getBody());
restResponseDTO.setMessage(tResponseEntity.getStatusCode().name());
restResponseDTO.setStatusCode(tResponseEntity.getStatusCodeValue());
}catch (Exception e){
restResponseDTO.setStatusCode(RestResponseDTO.UNKNOWN_ERROR);
restResponseDTO.setMessage(e.getMessage());
restResponseDTO.setData(null);
}
return restResponseDTO;
}
}
包装器 ExtractRestTemplate 很完美的更改了异常抛出的行为,让程序更具有容错性。在这里我们不考虑 ExtractRestTemplate 完成的功能,让我们把焦点放在 FilterRestTemplate 上,“实现 RestOperations 所有的接口”,这个操作绝对不是一时半会可以写完的,当时在重构之前我几乎写了半个小时,如下:
public abstract class FilterRestTemplate implements RestOperations {
protected volatile RestTemplate restTemplate;
protected FilterRestTemplate(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@Override
public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
return restTemplate.getForObject(url,responseType,uriVariables);
}
@Override
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {
return restTemplate.getForObject(url,responseType,uriVariables);
}
@Override
public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException {
return restTemplate.getForObject(url,responseType);
}
@Override
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
return restTemplate.getForEntity(url,responseType,uriVariables);
}
//其他实现代码略。。。
}
我相信你看了以上代码,你会和我一样觉得恶心反胃,后来我用 lombok 提供的代理注解优化了我的代码(@Delegate):
@AllArgsConstructor
public abstract class FilterRestTemplate implements RestOperations {
@Delegate
protected volatile RestTemplate restTemplate;
}
这几行代码完全替代上述那些冗长的代码。
原文: