在方法覆盖中返回继承的 class 而不是 superclass
returning inherited class instead of superclass in method overriding
我有一个特定的 class 结构,看起来像这样:
class Parent {
public Parent(int property) { /* use property */}
}
class Son extends Parent {
public Son(int parentProperty, String sonProperty) {
super(parentProperty);
/* use son property */
}
}
我想为这两个 class 创建构建器,这样:
class ParentBuilder {
protected int parentProperty;
public ParentBuilder parentProperty(int parentPropertyValue) {
parentPropertyValue = parentPropertyValue;
return this;
}
public Parent build() {
return new Parent(parentProperty);
}
}
class SonBuilder extends ParentBuilder {
private String sonProperty;
public SonBuilder sonProperty(String sonProperty) {
this.sonProperty = sonProperty;
return this;
}
@Override
public Son build() {
return new Son(parentProperty, sonProperty);
}
}
但这会导致以下问题:
SonBuilder sonBuilder = new SonBuilder();
sonBuilder.sonProperty("aString").build(); // this works and creates Son
sonBuilder.sonProperty("aString").parentProperty(1).build(); // this works and creates Parent instead of Son
sonBuilder.parentProperty(1).sonProperty("aString").build(); // this doesn't work
我意识到我在挑剔,这可以通过不返回 this
(即没有方法链接)来解决,但我想知道是否有一个优雅的解决方案。
编辑
"elegant" 这个词似乎有点混乱。
"elegant" 我的意思是一种允许方法链接且不涉及转换的解决方案。
您可以测试父 属性 是否存在于您的 build()
方法中。
return (parentProperty == null) ? new Parent(parentProperty, sonProperty) : new Son(sonProperty);
我不确定它是否可以被认为是优雅的,但你可以使用转换:
SonBuilder sonBuilder = new SonBuilder();
Son son1 = sonBuilder.sonProperty("aString").build();
Son son2 = (Son) sonBuilder.sonProperty("aString").parentProperty(1).build();
Son son3 = ((SonBuilder) sonBuilder.parentProperty(1)).sonProperty("aString").build();
第一点
sonBuilder.sonProperty("aString").parentProperty(1).build();
this works and creates Parent instead of Son
预期为 parentProperty()
returns a ParentBuilder
:
public ParentBuilder parentProperty(int parentPropertyValue) {...
并且 ParentBuilder.build()
创建一个 Parent
:
public Parent build() {
return new Parent(parentProperty);
}
第二点
sonBuilder.parentProperty(1).sonProperty("aString").build(); // this doesn't work
如第一点所述,parentProperty()
returns a ParentBuilder
.
ParentBuilder
当然没有sonProperty()
方法。
所以编译不了。
I'm wondering if there is an elegant solution.
一个优雅的解决方案不是使 SonBuilder
继承 ParentBuilder
,而是与 ParentBuilder
字段组合。
例如:
class SonBuilder {
private String sonProperty;
private ParentBuilder parentBuilder = new ParentBuilder();
public SonBuilder sonProperty(String sonProperty) {
this.sonProperty = sonProperty;
return this;
}
public SonBuilder parentProperty(int parentPropertyValue) {
parentBuilder.parentProperty(parentPropertyValue);
return this;
}
public Son build() {
return new Son(parentBuilder.parentProperty, sonProperty);
}
}
您可以这样创建 Son
:
SonBuilder sonBuilder = new SonBuilder();
Son son = sonBuilder.sonProperty("aString").parentProperty(1).build();
覆盖 SonBuilder
中的 parentProperty
:
@Override
public SonBuilder parentProperty(int parentPropertyValue) {
super.parentProperty(parentPropertyValue);
return this;
}
这是安全的,因为(只要)super.parentProperty
returns this
。如果你有不可变的构建器,你的覆盖也会不同。
或者使用泛型:
abstract class AParentBuilder<T extends Parent> {
protected int parentProperty;
public AParentBuilder<T> parentProperty(int parentPropertyValue) {
parentPropertyValue = parentPropertyValue;
return this;
}
abstract public T build();
}
class ParentBuilder extends AParentBuilder<Parent> {
@Override
public Parent build() {
return new Parent(parentProperty);
}
}
abstract class ASonBuilder<T extends Son> extends AParentBuilder<T> {
private String sonProperty;
public ASonBuilder<T> sonProperty(String sonProperty) {
this.sonProperty = sonProperty;
return this;
}
}
class SonBuilder extends ASonBuilder<Son> {
@Override
public Son build() {
return new Son(parentProperty, sonProperty);
}
}
另一种方法是在 subclass 方法中使用协变 return 类型。
保持 SonBuilder
扩展 ParentBuilder
但将 parentProperty()
覆盖为 return a SonBuilder
:
@Override
public SonBuilder parentProperty(int parentPropertyValue) {
super.parentProperty(parentPropertyValue);
return this;
}
您仍然可以通过这种方式创建 Son :
SonBuilder sonBuilder = new SonBuilder();
Son son = sonBuilder.sonProperty("aString").parentProperty(1).build();
现在 parentProperty(1)
调用 return 作为 SonBuilder
的子 class 方法。
这个解决方案可能看起来不错,但它有一个缺点。
它在 subclass 构建器和基础构建器之间建立了强耦合。
每次在父构建器中添加 setProperty 方法时,都必须在 subclass.
中覆盖它
否则,您仍然会遇到最初的问题:无法编译的代码或者它会实例化 Parent 而不是 Son class.
对于完全没有变化的 classes,它可能是可以接受的。
否则应避免。
使用组合的解决方案更好,因为它允许 SonBuilder
按照自己的节奏和规则发展。
我有一个特定的 class 结构,看起来像这样:
class Parent {
public Parent(int property) { /* use property */}
}
class Son extends Parent {
public Son(int parentProperty, String sonProperty) {
super(parentProperty);
/* use son property */
}
}
我想为这两个 class 创建构建器,这样:
class ParentBuilder {
protected int parentProperty;
public ParentBuilder parentProperty(int parentPropertyValue) {
parentPropertyValue = parentPropertyValue;
return this;
}
public Parent build() {
return new Parent(parentProperty);
}
}
class SonBuilder extends ParentBuilder {
private String sonProperty;
public SonBuilder sonProperty(String sonProperty) {
this.sonProperty = sonProperty;
return this;
}
@Override
public Son build() {
return new Son(parentProperty, sonProperty);
}
}
但这会导致以下问题:
SonBuilder sonBuilder = new SonBuilder();
sonBuilder.sonProperty("aString").build(); // this works and creates Son
sonBuilder.sonProperty("aString").parentProperty(1).build(); // this works and creates Parent instead of Son
sonBuilder.parentProperty(1).sonProperty("aString").build(); // this doesn't work
我意识到我在挑剔,这可以通过不返回 this
(即没有方法链接)来解决,但我想知道是否有一个优雅的解决方案。
编辑
"elegant" 这个词似乎有点混乱。
"elegant" 我的意思是一种允许方法链接且不涉及转换的解决方案。
您可以测试父 属性 是否存在于您的 build()
方法中。
return (parentProperty == null) ? new Parent(parentProperty, sonProperty) : new Son(sonProperty);
我不确定它是否可以被认为是优雅的,但你可以使用转换:
SonBuilder sonBuilder = new SonBuilder();
Son son1 = sonBuilder.sonProperty("aString").build();
Son son2 = (Son) sonBuilder.sonProperty("aString").parentProperty(1).build();
Son son3 = ((SonBuilder) sonBuilder.parentProperty(1)).sonProperty("aString").build();
第一点
sonBuilder.sonProperty("aString").parentProperty(1).build();
this works and creates Parent instead of Son
预期为 parentProperty()
returns a ParentBuilder
:
public ParentBuilder parentProperty(int parentPropertyValue) {...
并且 ParentBuilder.build()
创建一个 Parent
:
public Parent build() {
return new Parent(parentProperty);
}
第二点
sonBuilder.parentProperty(1).sonProperty("aString").build(); // this doesn't work
如第一点所述,parentProperty()
returns a ParentBuilder
.
ParentBuilder
当然没有sonProperty()
方法。
所以编译不了。
I'm wondering if there is an elegant solution.
一个优雅的解决方案不是使 SonBuilder
继承 ParentBuilder
,而是与 ParentBuilder
字段组合。
例如:
class SonBuilder {
private String sonProperty;
private ParentBuilder parentBuilder = new ParentBuilder();
public SonBuilder sonProperty(String sonProperty) {
this.sonProperty = sonProperty;
return this;
}
public SonBuilder parentProperty(int parentPropertyValue) {
parentBuilder.parentProperty(parentPropertyValue);
return this;
}
public Son build() {
return new Son(parentBuilder.parentProperty, sonProperty);
}
}
您可以这样创建 Son
:
SonBuilder sonBuilder = new SonBuilder();
Son son = sonBuilder.sonProperty("aString").parentProperty(1).build();
覆盖 SonBuilder
中的 parentProperty
:
@Override
public SonBuilder parentProperty(int parentPropertyValue) {
super.parentProperty(parentPropertyValue);
return this;
}
这是安全的,因为(只要)super.parentProperty
returns this
。如果你有不可变的构建器,你的覆盖也会不同。
或者使用泛型:
abstract class AParentBuilder<T extends Parent> {
protected int parentProperty;
public AParentBuilder<T> parentProperty(int parentPropertyValue) {
parentPropertyValue = parentPropertyValue;
return this;
}
abstract public T build();
}
class ParentBuilder extends AParentBuilder<Parent> {
@Override
public Parent build() {
return new Parent(parentProperty);
}
}
abstract class ASonBuilder<T extends Son> extends AParentBuilder<T> {
private String sonProperty;
public ASonBuilder<T> sonProperty(String sonProperty) {
this.sonProperty = sonProperty;
return this;
}
}
class SonBuilder extends ASonBuilder<Son> {
@Override
public Son build() {
return new Son(parentProperty, sonProperty);
}
}
另一种方法是在 subclass 方法中使用协变 return 类型。
保持 SonBuilder
扩展 ParentBuilder
但将 parentProperty()
覆盖为 return a SonBuilder
:
@Override
public SonBuilder parentProperty(int parentPropertyValue) {
super.parentProperty(parentPropertyValue);
return this;
}
您仍然可以通过这种方式创建 Son :
SonBuilder sonBuilder = new SonBuilder();
Son son = sonBuilder.sonProperty("aString").parentProperty(1).build();
现在 parentProperty(1)
调用 return 作为 SonBuilder
的子 class 方法。
这个解决方案可能看起来不错,但它有一个缺点。
它在 subclass 构建器和基础构建器之间建立了强耦合。
每次在父构建器中添加 setProperty 方法时,都必须在 subclass.
中覆盖它
否则,您仍然会遇到最初的问题:无法编译的代码或者它会实例化 Parent 而不是 Son class.
对于完全没有变化的 classes,它可能是可以接受的。
否则应避免。
使用组合的解决方案更好,因为它允许 SonBuilder
按照自己的节奏和规则发展。