我想解决这样的问题。我有一些抽象类和一个带有setter的具体类,它们返回该类的实例:
@MappedSuperclass
public abstract class BaseEntity implements Serializable {
private Integer id;
public Integer getId() {
return id;
}
public BaseEntity setId(Integer id) {
this.id = id;
return this;
}
}
下一篇摘要:
@MappedSuperclass
public abstract class NamedEntity extends BaseEntity {
private String name;
public String getName() {
return name;
}
public NamedEntity setName(String name) {
this.name = name;
return this;
}
}
最后是一个具体的类别:
@Entity
public class Person extends NamedEntity {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
我想使用这种构建器,但在当前的设置中,由于父设置的返回类型不同,它不起作用
public Person build() {
Person person = new Person()
.setId(1); //return BaseEntity instead of Person
.setName("name") //returns NamedEntity instead of Person
.setAddress("foo"); //return Person!
return person;
}
当然,有一个替代setter的变通方法,但是。。。。可以使用泛型以其他方式完成吗?
@Override
public Person setId(Integer id) {
super.setId(id);
return this;
}
@Override
public Person setName(String name) {
super.setName(name);
return this;
}
感谢您的建议我知道构建器模式,但在这种特殊情况下,解决方法与重写方法setId
和setName
相同重点是:setId
方法可能会返回从调用该方法的子类的实例
比方说,我想把一个复杂的对象放在我的构建器中(为什么不呢?(:
public class Person extends NamedEntity {
private String address;
... getters/setters
public Builder builder() {
return new Builder();
}
public final static class Builder {
private final Person person;
private Long id;
private String name;
private String address;
private Builder() {
this.person = new Person();
}
public Builder withId(Long id) {
person.setId(id);
return this;
}
..... other setters
public Builder withDto(PersonDTO dto) {
person
.setId(dto.getId())
.setName(dto.getName())
.setAddress(dto.getAddress()
}
public Person build() {
return person;
}
}
}
正如您可能猜测的那样,person.setId
返回BaseEntity 的实例
您可以使用与enums(Enum
(相同的技巧,enums是子类的泛型类型参数。
@MappedSuperclass
public abstract class BaseEntity<E extends BaseEntity<E>> implements Serializable {
private Integer id;
public Integer getId() {
return id;
}
protected final E getThis() {
return this;
}
public E setId(Integer id) {
this.id = id;
return getThis();
}
}
@MappedSuperclass
public abstract class NamedEntity<E extends NamedEntity<E>> extends BaseEntity<E> {
private String name;
public String getName() {
return name;
}
public E setName(String name) {
this.name = name;
return getThis();
}
}
对于Person的子类,您不需要继续使用此模式。
@Entity
public class Person extends NamedEntity<Person> {
private String address;
public String getAddress() {
return address;
}
public Person setAddress(String address) {
this.address = address;
return this;
}
}
现在你可以做_
Person einstein = new Person()
.setId(76)
.setName("Albert")
.setAddress("Princeton, New Jersey");
另一种选择是Builder模式,但它也有同样的继承问题,最终可能会出现从父Builder类继承的*.Builder类。
我甚至会说,仅仅为了一个流畅的API(链式调用(,这个锅炉板代码是不值得的。例如,标准API几乎不需要使用创建的对象,并且为setter传递的值也必须来自某些代码。
setters还暗示类是可变的。如果大多数字段都是不可变的,那就更好了。实体类不切实际,但setter是一个丑陋的初始化。尽可能使用不带setter的构造函数/构造函数。
您可以通过引入嵌套类Builder
来实现Builder模式,该类具有一组自返回方法(即返回Builder
的实例(,这些方法可以以流畅的方式链接。
方法Builder.build()
应该返回一个Person
的实例。
注意
您的实体的设置者可以是void
。
这就是实现的样子:
public class Person extends NamedEntity {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public static class Builder {
private Person person;
public Builder() {
this.person = new Person();
}
public Builder name(String name) {
person.setName(name);
return this;
}
public Builder address(String address) {
person.setAddress(address);
return this;
}
public Builder id(Integer id) {
person.setId(id);
return this;
}
public Person build() {
return person;
}
}
}
用法示例:
Person person = new Person.Builder()
.name("Alice")
.address("Wonderland")
.id(1)
.build();
注意:
- 可能有多种方法可以获得
Builder
的实例。您可以在Person
类中引入一个返回新Builder
的静态方法builder()
,或者像withName(String)
、withId(Integer)
这样的静态方法也可能很方便(要获得灵感,请查看SpringSecurity中的User
类( - 当使用不可变对象拨号时,
Builder
类应该复制目标类的所有字段,而不是保留对目标对象的引用。在这种情况下,方法build()
将负责构建目标类型的实例