Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/383.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 从父类返回子类_Java_Generics_Inheritance_Types_Casting - Fatal编程技术网

Java 从父类返回子类

Java 从父类返回子类,java,generics,inheritance,types,casting,Java,Generics,Inheritance,Types,Casting,我有一个生成器类,它从大多数方法返回自己,以允许菊花链。为了使这一点在子类中起作用,我希望父方法返回子类的实例,以便子方法可以链接到最后 public class BaseBuilder<T extends BaseBuilder<T>> { public T buildSomething() { doSomeWork(); /* OPTION #1: */ return this; // "Type mismatch: c

我有一个生成器类,它从大多数方法返回自己,以允许菊花链。为了使这一点在子类中起作用,我希望父方法返回子类的实例,以便子方法可以链接到最后

public class BaseBuilder<T extends BaseBuilder<T>> {
    public T buildSomething() {
        doSomeWork();
        /* OPTION #1: */ return this;     // "Type mismatch: cannot convert from BaseBuilder<T> to T"
        /* OPTION #2: */ return T.this;   // "Type mismatch: cannot convert from BaseBuilder<T> to T"
        /* OPTION #3: */ return (T) this; // "Type safety: Unchecked cast from SqlBuilder<T> to T"
    }
}

public class ChildBuilder extends BaseBuilder<ChildBuilder> {}
公共类基类生成器{
公共建筑{
doSomeWork();
/*选项#1://返回此;//“类型不匹配:无法从BaseBuilder转换为T”
/*选项#2://return T.this;//“类型不匹配:无法从BaseBuilder转换为T”
/*选项#3://return(T)this;//“类型安全:未选中从SqlBuilder转换为T”
}
}
公共类ChildBuilder扩展了BaseBuilder{}

选项#1和#2会导致编译错误,选项#3会导致警告(尽管可以使用
@SuppressWarnings(“未选中”)
抑制该警告)。这里有更好的方法吗?如何安全地将Basebuilder向下转换为Childbuilder?

而不是声明要返回的方法
T
-将其声明为返回
Basebuilder

public BaseBuilder buildSomething() {
...
class BuilderA extends BaseBuilder<BuilderA> {
   BuilderA buildSomethingA() { return this; }
}

class BuilderB extends BaseBuilder<BuilderB> {
   BuilderB buildSomethingB() { return this; }
}
由于
T
扩展了
BaseBuilder
,但在编译时仍然未知,因此我相信这是您可以做到的最好的折衷方法

如果您(在编译时)确切地知道要返回的类型,您可以简单地返回它,但如果不知道,您将不得不向下转换,您将继续得到
“类型安全:未选中的强制转换”
,如果您能证明向下转换是有效的,那么对
SuppressWarnings
来说这是非常好的


请参阅Josh Bloch的相关内容。

一种可能性是利用Java支持协变返回类型的事实。例如,此代码是合法的:

class BaseBuilder {
    BaseBuilder buildSomething() { (...) return this; }
}

class ChildBuilder extends BaseBuilder {
    @Override  // Notice the more specific return type
    ChildBuilder buildSomething() { (...) return this; }
}

void main() {
    BaseBuilder  x = new BaseBuilder ().buildSomething().anotherOperation();
    ChildBuilder y = new ChildBuilder().buildSomething().anotherOperation();
}

否则,选项#3是真正实现所需的唯一方法。它允许超类方法直接返回子类类型,以便您可以调用子类方法:

@SuppressWarnings("unchecked")   // Ugly.
class Base<T extends Base<T>> {  // Ugly.
    public T alpha() { return (T)this; }
    public T delta() { return (T)this; }
}

class Child extends Base<Child> {  // Clean.
    // No need to override/redefine alpha() and delta() in child.
    public Child gamma() { return this; }
}

void main(String[] args) {
    Child x = new Child();
    x.alpha().gamma();  // This works because alpha() returns Child.
}
@SuppressWarnings(“未选中”)//丑陋。
类基类{//。
公共T alpha(){return(T)this;}
公共T delta(){return(T)this;}
}
类子级扩展了基{//Clean。
//无需重写/重新定义子对象中的alpha()和delta()。
公共子gamma(){返回此;}
}
void main(字符串[]args){
Child x=新的Child();
x、 alpha().gamma();//这是因为alpha()返回Child。
}

在我看来,需要更改基本生成器签名
BaseBuilder

我可以想象T是指正在构建的类型
BaseBuilder
ChildBuilder扩展BaseBuilder

在ChildBuilder中重写方法仍然有效-返回
this
。从构建方法返回
T

public class BaseBuilder<T extends ComplexObject> {
   public BaseBuilder<T> withComplexThing() {
        return this;
   }

   public T build() {
   }

}

public class ChildBuilder extends BaseBuilder<MoreComplexObject> {
   public ChildBuilder withComplexThing() {
        return this;
   }

   public MoreComplexObject build() {
   }
}
公共类基类生成器{
带有complexthing()的公共BaseBuilder{
归还这个;
}
公共T构建(){
}
}
公共类ChildBuilder扩展了BaseBuilder{
带有complexthing()的公共儿童生成器{
归还这个;
}
public MoreComplexObject build(){
}
}

没关系,只需抑制警告

如果你必须是一个纯粹主义者,这里有一个解决方案:

abstract public class BaseBuilder<T...> 
{
    abstract protected T getThis();

    public T buildSomething() 
       ...
          return getThis();


...

public class ChildBuilder extends BaseBuilder<ChildBuilder> 
{
    @Override
    protected ChildBuilder getThis(){ return this; } 
}

声明
ChildBuilder extensed BaseBuilder
以某种方式指示代码气味,似乎违反了DRY。在本例中,
BaseBuilder
只能使用
ChildBuilder
进行参数化,而不能使用其他任何东西,因此它应该是多余的

我宁愿重新思考我是否真的想要超越这个架构,我会尝试将所有来自子构建器的方法放入
BaseBuilder
。然后我可以简单地从所有支持链接的方法返回
this

如果我仍然认为我将受益于将特定的构建器方法组分离到它们自己的类中,那么我将优先考虑组合,因为不建议仅为代码重用应用继承

假设我们有两个子类
BaseBuilder

public BaseBuilder buildSomething() {
...
class BuilderA extends BaseBuilder<BuilderA> {
   BuilderA buildSomethingA() { return this; }
}

class BuilderB extends BaseBuilder<BuilderB> {
   BuilderB buildSomethingB() { return this; }
}
如果不将子类方法移动到
BaseBuilder
,我们将无法实现这一点;但是想象一下还有
BuilderC
,这些方法对它们没有意义,不应该从
BaseBuilder
继承

如果我们将这两个方法移到超类,下一次再移到其他三个方法,下一次……我们将得到一个超类,负责整个层次结构90%的职责,并有大量代码,如:

if ((this instanceof BuilderB) && !flag1 && flag2) {
   ...
} else if ((this instanceof BuilderC) && flag1 && !flag2 && thing != null) {
   ...
} else if ...
我更喜欢的解决方案是类似DSL的:

builder.buildSomething1().buildSomething2()
   .builderA()
      .buildSomethingA1().buildSomethingA2()
   .end()
   .buildSomething3()
   .builderB()
      .buildSomethingB()
   .end();
此处
end()
返回
builder
实例,以便您可以链接其更多方法或启动新的子生成器

通过这种方式,(子)构建器可以继承他们需要继承的任何内容(否则他们必须只扩展
BaseBuilder
),并且可以拥有自己有意义的层次结构或组合。

Cast-in option#3不安全,因为将编译以下类(这是开发人员的责任):

public类ChildBuilder扩展了BaseBuilder{}
^^^^^^^^^^^
一个常见的解决方案是向子类询问它们的
this

公共抽象类BaseBuilder{
保护抽象T getThis();
公共建筑{
返回getThis();
}
}
公共类ChildBuilder扩展了BaseBuilder{
@凌驾
受保护的ChildBuilder getThis(){
归还这个;
}
}

这不应该是公共类BaseBuilder为什么泛型类型引用同一类?=>ChildBuilder扩展BaseBuilder@6ton,因为唯一有效的泛型类型是继承BaseBuilder的类型。例如,
BaseBuilder
没有意义。仍然不确定您为什么需要签名-我发布了一个带有Rejustics的答案a谢谢,你提出了一些好的观点。
end()
建议是个好主意!