【问题标题】:Java generic builderJava 通用构建器
【发布时间】:2016-08-18 10:14:36
【问题描述】:

假设我需要一些DerivedBuilder 来扩展一些BaseBuilder。基础构建器有一些方法,如foo(返回BaseBuilder)。派生构建器具有方法bar。方法bar 应该在方法foo 之后调用。为了做到这一点,我可以像这样覆盖DerivedBuilder 中的foo 方法:

@Override
public DerivedBuilder foo() {
    super.foo();
    return this;
}

问题是BaseBuilder 有很多像foo 这样的方法,我必须覆盖它们中的每一个。我不想这样做,所以我尝试使用泛型:

public class BaseBuilder<T extends BaseBuilder> {
    ...

    public T foo() {
        ...
        return (T)this;
    }
}

public class DerivedBuilder<T extends DerivedBuilder> extends BaseBuilder<T> {
    public T bar() {
        ...
        return (T)this;
    }
}

但问题是我还是不会写

new DerivedBuilder<DerivedBuilder>()
        .foo()
        .bar()

尽管T 这里是DerivedBuilder。我该怎么做才能不覆盖很多功能?

【问题讨论】:

  • 使用这种泛型方法,您可以致电:new DerivedBuilder&lt;DerivedBuilder&gt;().foo().bar()。它将工作并首先执行foo,然后执行bar。如果你想调用BaseBuilder的更多方法,最后你想调用DerivedBuilder方法,那么这是不可能的,因为第二次,方法返回BaseBuilder的引用,这样你就不能调用DerivedBuilder' s 方法。
  • @Sandeep.KI 试图执行它并且编译器抱怨bar 没有在BaseBuilder 中定义,因为那时我做了.foo().bar() 在执行@987654351 之后编译器唯一知道T @ 是这个T 扩展了BaseBuilder
  • thisthisthis...
  • @user1803551 我不明白这如何解决我的问题。在您的链接中描述了一些方法来确保当我的问题是关于如何不显式调用它时调用超级方法。
  • 他们解决了同样的问题,并且(其中一些)答案回答了您的问题。 “确保调用超级方法的某些方法”与“如何不显式调用它”之间没有矛盾 - 确保调用它可以在某种程度上不显式地完成,这就是那里的答案。跨度>

标签: java generics builder-pattern


【解决方案1】:

你的问题是DerivedBuilder的定义:

class DerivedBuilder<T extends DerivedBuilder>;

然后用类型擦除参数 new DerivedBuilder&lt;DerivedBuilder&lt;...what?...&gt;&gt;() 实例化它。

您需要一个完全定义的派生类型,如下所示:

public class BaseBuilder<T extends BaseBuilder<T>> {
    @SuppressWarnings("unchecked")
    public T foo() {
        return (T)this;
    }
}

public class DerivedBuilder extends BaseBuilder<DerivedBuilder> {
    public DerivedBuilder bar() {
        return this;
    }
}

检查ideone.com

【讨论】:

  • 是否还有可能进一步推导出DerivedBuilder,例如SubDerivedBuilderSubDerivedBuilderM&lt;...&gt; extends DerivedBuilder&lt;...&gt;
  • @MCEmperor 是的,但是您需要使所有最派生的类型都将完整的类型传递给它们的基类。检查this fork
【解决方案2】:

除了BeyelerStudios's answer之外,如果你想进一步嵌套,你可以使用这个:

class Base<T extends Base<?>> {
    public T alpha() { return (T) this; }
    public T bravo() { return (T) this; }
    public T foxtrot() { return (T) this; }
}

class Derived<T extends Derived<?>> extends Base<T> {
    public T charlie() { return (T) this; }
    public T golf() { return (T) this; }
}

class FurtherDerived<T extends FurtherDerived<?>> extends Derived<T> {
    public T delta() { return (T) this; }
    public T hotel() { return (T) this; }
}

class MuchFurtherDerived<T extends MuchFurtherDerived<?>> extends FurtherDerived<T> {
    public T echo() { return (T) this; }
}

public static void main(String[] args) {
    new MuchFurtherDerived<MuchFurtherDerived<?>>()
        .alpha().bravo().charlie().delta().echo().foxtrot().golf().hotel()
        .bravo().golf().delta().delta().delta().hotel().alpha().echo()
        .echo().alpha().hotel().foxtrot();
}

【讨论】:

    【解决方案3】:

    我在这里做了一个Class.cast(this),而不是return (T) this;

    实现:

    BaseBuilder.build(ExtendedBuilder.class).foo().bar().foo().bar();
    

    层次结构中的每个类都需要知道实际的最终子类,因此我选择在基类中创建一个工厂方法build

    this 转换为实际的孩子也是在基类的final 方法中完成的,提供return me();

    class BaseBuilder<B extends BaseBuilder<B>> {
    
        protected Class<B> type;
    
        public static <T extends BaseBuilder<T>> T build(Class<T> type) {
            try {
                T b = type.newInstance();
                b.type = type;
                return b;
            } catch (InstantiationException | IllegalAccessException e) {
                throw new IllegalStateException(e);
            }
        }
    
        protected final B me() {
            return type.cast(this);
        }
    
        B foo() {
            System.out.println("foo");
            return me();
        }
    }
    
    class ExtendedBuilder extends BaseBuilder<ExtendedBuilder> {
    
        ExtendedBuilder bar() {
            System.out.println("bar");
            return me();
        }
    }
    

    【讨论】:

    • 现在你有了一个建造者工厂。此外,type 字段和type.cast(this) 毫无意义,因为它没有提供任何额外的编译时安全性,并且使整个事情变得复杂。
    • @XetraSu 提议static class 可能适合他们的用法。
    【解决方案4】:

    我从你的问题中了解到,方法 foo() 应该在方法 bar() 之前执行。
    如果正确,您可以应用模板设计模式。
    在 BaseBuilder 中创建抽象方法栏。
    还有一种新方法说“模板”。方法模板将定义顺序——首先执行 foo(),然后执行 bar()。
    DerivedBuilder 将提供方法栏的实现。

    public abstract class BaseBuilder {
    
        public void foo(){
            System.out.println("foo");
        }
    
        public abstract void bar();
    
        public void template(){
            foo();
            bar();
        }
    }
    
    public class DerivedBuilder extends BaseBuilder{
    
        @Override
        public void bar() {
            System.out.println("bar");      
        }
    
        public static void main(String[] args) {
            BaseBuilder builder = new DerivedBuilder();
            builder.template();
        }
    }
    


    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多