Java 如何让子类返回它们自己的类型?

Java 如何让子类返回它们自己的类型?,java,generics,Java,Generics,这里我们有多个使用相关语法的自定义查询DSL。我正在创建一个AbstractBuilder,这样所有的通用性都可以在一个地方编写。问题是,它会导致方法链接出现问题。当我尝试将中的方法链接到AbstractBuilder中,以使其成为子类时,如果不强制转换,它将无法工作 在这些课程中: class AbstractBuilder{ protected final StringBuilder bldr = new StringBuilder(); AbstractBuilder a

这里我们有多个使用相关语法的自定义查询DSL。我正在创建一个
AbstractBuilder
,这样所有的通用性都可以在一个地方编写。问题是,它会导致方法链接出现问题。当我尝试将中的方法链接到
AbstractBuilder
中,以使其成为子类时,如果不强制转换,它将无法工作

在这些课程中:

class AbstractBuilder{
    protected final StringBuilder bldr = new StringBuilder();

    AbstractBuilder addValue( String name, String value ){
        bldr.append( name ).append( '=' ).append( value )append( ',' );
        return this;
    }

    String toString(){
        return bldr.toString();
    }
}

class IntBuilder extends AbstractBuilder{

    IntBuilder addValue( String name, int value ){
        bldr.append( name ).append( '=' ).append( value )append( ',' );
        return this;
    }
}
这很有效

new IntBuilder().addValue( "age", 12 ).addValue( "name", "Bruce" ).toString();` but `new IntBuilder().addValue( "name", "Bruce" ).addValue( "age", 12 ).toString();
除非你做了一个丑陋的演员,比如:

((IntBuilder) (new IntBuilder().addValue( "name", "Bruce" ))).addValue( "age", 12 ).toString();
现在我想我可以覆盖每个方法,并通过调用它们的父方法来实现它们(通过
super.addValue(name,value);
),但这真的很难看


否则,如何让每个方法返回当前类而不是定义它的类?

您可以使用泛型实现这一点

class BaseBuilder<B extends BaseBuilder>{
    protected final StringBuilder bldr = new StringBuilder();
    private Class<B> builderImplementationClass;

    protected BaseBuilder( Class<B> builderImpl ){
        builderImplementationClass = builderImpl;

        if( this.getClass() != builderImpl )
            throw new IllegalStateException( "Should match." );
    }

    B addValue( String name, String value ){
        bldr.append( name ).append( '=' ).append( value )append( ',' );
        return builderImplementationClass.cast( this );
    }

    String toString(){
        return bldr.toString();
    }
}

class IntBuilder extends AbstractBuilder<IntBuilder>{

    public IntBuilder(){
        super( IntBuilder.class );
    }

    IntBuilder addValue( String name, int value ){
        bldr.append( name ).append( '=' ).append( value )append( ',' );
        return this;
    }
}
classbasebuilder{
受保护的最终StringBuilder bldr=新StringBuilder();
私有类builderImplementationClass;
受保护的BaseBuilder(类builderImpl){
builderImplementClass=builderImpl;
if(this.getClass()!=builderImpl)
抛出新的非法状态异常(“应匹配”);
}
B addValue(字符串名称、字符串值){
bldr.append(name).append('=').append(value)append(',');
返回builderImplementationClass.cast(this);
}
字符串toString(){
返回bldr.toString();
}
}
类IntBuilder扩展了AbstractBuilder{
公共IntBuilder(){
超级(IntBuilder.class);
}
IntBuilder addValue(字符串名称、int值){
bldr.append(name).append('=').append(value)append(',');
归还这个;
}
}

基本上,您可以让所有方法返回类型为
B
的值,并确保
B
设置为您当前的实现

ArtB的解决方案是好的,但您可以通过以下操作使其更简单:

private static class AbstractBuilder <T extends AbstractBuilder<T>> {
    protected StringBuilder bldr = new StringBuilder();

    public T addValue( String name, String value ){
        bldr.append( name ).append( '=' ).append( value ).append(',');
        @SuppressWarnings("unchecked")
        T result = (T)this;
        return result;
    }

    public String toString(){
        return bldr.toString();
    }
}

private static class IntBuilder extends AbstractBuilder<IntBuilder> {

    public IntBuilder addValue( String name, int value ){
        bldr.append( name ).append( '=' ).append( value ).append(',');
        return this;
    }
}
私有静态类AbstractBuilder{
受保护的StringBuilder bldr=新StringBuilder();
公共T addValue(字符串名称、字符串值){
bldr.append(name).append('=').append(value).append(',');
@抑制警告(“未选中”)
T result=(T)这个;
返回结果;
}
公共字符串toString(){
返回bldr.toString();
}
}
私有静态类IntBuilder扩展了AbstractBuilder{
公共IntBuilder addValue(字符串名称、int值){
bldr.append(name).append('=').append(value).append(',');
归还这个;
}
}

注意,我还修复了两个私有StringBuilder变量的错误,这几乎肯定不是您想要的。

您有一个更大的问题:在具有受保护访问权限的基类中有两个StringBuilder,而不是一个。即使是您的简单示例也不会返回正确的结果:)我从头开始创建了一个示例代码,因此我从未运行过它,但获得了很好的效果!;)当然可以,但编译时不会收到
未经检查的转换
警告?与在类上使用
cast()
方法相比,这是常规强制转换的缺点。当然,但是强制转换的作用完全相同,只是编译器没有给出任何警告。您可以在基类中抛出一个
@SuppressWarnings(“unchecked”)
,这是一个好主意。@Voo:
.cast()
的作用并不完全相同,因为它在运行时检查它,如果类型不匹配,则抛出一个异常,因此它不是一个未经检查的强制转换。这里的未经检查强制转换是不安全的(即,
不一定是
T
的实例。)@newacct如果
不是T,则此处会出现运行时异常,就像在
cast()中一样
version。唯一的区别是什么类型的异常,但很明显这是可以调整的。这里真正有趣的问题是,您必须如何定义类,以便您也可以从
IntBuilder
进行扩展-不要认为这实际上是可能的,而不必给调用代码增加一些负担(通过使其必须指定泛型绑定本身)或者将IntBuilder重构为一个抽象类。我认为,如果你添加一个
受保护的
构造函数进行链接,你就可以将
IntBuilder
也设置为泛型。是的,问题是如果你将
IntBuilder
设置为泛型,你要么将其设置为一个抽象类,要么拥有一个明确的
IntBuilder2
class从它继承(这会删除泛型),或者该类的用户必须使用
new IntBuilder()
调用它。第一个假设我们以后可以进行如此大的更改,而第二个则非常糟糕。这也是我想到的两件事——在这种情况下,可能最好使用工厂。