Java generic builder

предположим, мне нужен DerivedBuilder в будущем BaseBuilder. Base builder имеет некоторый метод, как foo (который возвращает BaseBuilder). Производный конструктор имеет метод bar. Метод bar следует вызывать после метода foo. Для этого я могу переопределить foo метод DerivedBuilder такой:

@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. Что я могу сделать, чтобы не переопределять множество функций?

4 ответов


ваша проблема заключается в определении DerivedBuilder:

class DerivedBuilder<T extends DerivedBuilder>;

а затем создание экземпляра с типом стирается аргумент new DerivedBuilder<DerivedBuilder<...what?...>>().

вам понадобится полностью определенный производный тип, например:

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.


кроме BeyelerStudios это, Если вы хотите гнездиться дальше, вы можете просто использовать это:

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();
}

вместо литья return (T) this; Я здесь сделал Class.cast(this).

реализовать:

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

каждый класс в иерархе должен знать фактический конечный дочерний класс, поэтому я решил сделать метод фабрики build в базовом классе.

литой this фактическому ребенку делается в окончательном методе базового класса тоже, предоставляя 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();
    }
}

что я понимаю из вашего вопроса, так это то, что метод 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();
    }
}


Надеюсь, это помогает.