@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;
}

这几行代码完全替代上述那些冗长的代码。

原文: