Java 常用构建对象的三种方式
1. 前言
当我们面对具有大量可选成员变量的 Java 类时,创建这些对象的最佳方法是什么?通常有三种方法: 伸缩构造函数,JavaBean模式和构建器模式。
2. 构造函数
UserInfo userInfo1 = new UserInfo("felord.cn", 28);
UserInfo xxxxxx = new UserInfo("felord.cn", "xxxxxx", 28);
UserInfo xxxxxx1 = new UserInfo("felord.cn", "xxxxxx", 28, LocalDateTime.now());
构造函数通常需要可伸缩性,也就是参数列表需要重载。有些时候我不得不传入null
进行初始化。
// 不合理的构造使用示范
UserInfo xxxxxx = new UserInfo(null, null, 28);
而且不能直观看出这些参数所代表的的含义,这有可能引发致命的错误,我们将同类型的username
和address
互换位置依然成功初始化了对象,不会显式的引发构建错误,这是不合理的。
另外如果参数列表比较长,有七八个的话,代码是非常冗长的。
难道构造函数一无是处吗,当然不是。胖哥在使用构造参数时会确保构造的参数列表不会太长,而且如果参数是可选的话,不会将其置于构造函数中的。另外构建不可变对象使用构造函数也是极好的。
3. JavaBean
这种方式是最常用的创建对象的方法。只需要使用无参构造函数,然后为每个成员变量设置setter
方法。
UserInfo userInfo = new UserInfo();
userInfo.setUsername("felord.cn");
userInfo.setAge(28);
这种方式之所以使用非常普遍是因为很多知名框架需要你采用这种模式,比如JSON类库Jackson、Spring Framework还有绝大部分的ORM框架。
大多数情况下这种方式是可以胜任的。它的缺点在于我们需要两步来完成对象的创建工作,另外它缺乏创建不可变对象的能力。
4. 构建器
构建器其实在我之前的文章多次用到,Spring Security对HttpSecurity
的配置就用到了该模式。构建器不仅获得了伸缩构造函数的安全性,而且可读性更好。
我们需要在目标对象(这里是UserInfo
)内部创建了一个静态类,通常简单地称为Builder
。Builder
声明了一系列方法来设置对象属性的值,然后将其返回Builder
本身,完成所有调用后,我们调用Builder
的无参build
方法进行目标对象的初始化。
public class UserInfo {
private String username;
private String address;
private Integer age;
private LocalDateTime addTime;
// 私有化无参构造
private UserInfo() {
}
public static class Builder {
private String username;
private Integer age;
private String address;
private LocalDateTime addTime;
public Builder username(String username) {
this.username = username;
return this;
}
public Builder address(String address) {
this.address = address;
return this;
}
public Builder age(Integer age) {
this.age = age;
return this;
}
public Builder addTime(LocalDateTime addTime) {
this.addTime = addTime;
return this;
}
public UserInfo build() {
UserInfo userInfo = new UserInfo();
userInfo.username = this.username;
userInfo.address = this.address;
userInfo.age = this.age;
userInfo.addTime = this.addTime;
return userInfo;
}
}
// 省略 getter
}
然后初始化对象就可以这么写:
UserInfo userInfo = new UserInfo.Builder()
.username("felord.cn")
.address("xxxxxx")
.age(28)
.addTime(LocalDateTime.now())
.build();
这种写法首先很流畅,而且可读性更高,同时灵活度也得到了保证,可选参数更易处理。但是这种模式增加了我们代码的书写难度,需要进行一些额外的定义。当然你可以借助于Lombok框架的@Builder
注解来直接使用构建器模式,但是不是每个人都喜欢Lombok。
由于常用的第三方框架的原因,这种写法的使用场景并不是那么宽泛。通常在定义一些配置时使用它。可参考在这篇文章的做法。
5. 总结
往期推荐