Java inherited Fluent method return type in multiple level hierarchies

天涯浪子 提交于 2019-12-02 01:36:49
Paul Bellora

This is your inheritance hierarchy:

As you can see, some of these types are inheriting the same interface type more than once. In fact, BarImpl implements BaseFoo four times over, and some of the inheritance chains provide differing arguments for its type parameter S. It can be said that BarImpl implements the following:

  • BaseFoo<T, Foo<T>> (via Foo)
  • BaseFoo<T, Foo<T>> (via FooImpl)
  • BaseFoo<T, Bar<T>> (via Bar)
  • BaseFoo<T, Bar<T>> (via BarImpl)

The same interface cannot be implemented with different type arguments, so you get a compiler error.

As I pointed out on your followup question, my answer here discusses how to properly emulate the "self-type" to implement a hierarchical fluent builder pattern like you're trying to do. In it, I point out the need to maintain a variable "self-type" (S in your code) in all intermediate types - only resolving it with a "leaf class" that is understood to be final. Your code is violating that rule because the intermediate types Foo, Bar, and FooImpl are prematurely resolving S.

The following solution resolves the issue:

static interface BaseFoo<T, S extends BaseFoo<T, S>> {
    S foo();
}

static interface Foo<T, S extends Foo<T, S>> extends BaseFoo<T, S> {
    void foo1();
}

static interface BaseBar<T, S extends BaseBar<T, S>> extends BaseFoo<T, S> {
    S bar();
}

static interface Bar<T, S extends Bar<T, S>> extends BaseBar<T, S> {
    void bar1();
}

static abstract class AbstractFooBase<T, S extends BaseFoo<T, S>> implements BaseFoo<T, S> {
    abstract void internalFoo();
    @Override
    public S foo() {
        internalFoo();
        return (S)this;
    }
}

static abstract class AbstractIntermediateFoo<T, S extends AbstractIntermediateFoo<T, S>> extends AbstractFooBase<T, S> implements Foo<T, S> {
    @Override
    void internalFoo() {
        System.out.println("inside FooImpl::internalFoo()");
    }

    @Override
    public void foo1() {
        System.out.println("inside FooImpl::foo1()");
    }
}

static final class FooImpl<T> extends AbstractIntermediateFoo<T, FooImpl<T>> { }

static abstract class AbstractBarBase<T, S extends AbstractBarBase<T, S>> extends AbstractIntermediateFoo<T, S> implements BaseBar<T, S> {
    abstract void internalBar();
    @Override
    public S bar() {
        internalBar();
        return (S)this;
    }
}

static final class BarImpl<T> extends AbstractBarBase<T, BarImpl<T>> implements Bar<T, BarImpl<T>> {
    @Override
    void internalBar() {
        System.out.println("inside BarImpl::internalBar()");
    }

    @Override
    public void bar1() {
        System.out.println("inside BarImpl::bar1()");
    }
}

public static void main(String[] args) {
    FooImpl<String> foo = new FooImpl<String>();
    foo.foo().foo1();

    BarImpl<Boolean> bar = new BarImpl<Boolean>();
    bar.foo().bar1();
}

My changes were the following:

  • Maintain S in Foo
  • Maintain S in Bar
  • Split FooImpl into the following:
    • AbstractIntermediateFoo, which is abstract, maintains S, and implements internalFoo and foo1.
    • FooImpl, which is concrete, final, and resolves S.
  • Make BarImpl final.
  • In main, declare foo and bar as FooImpl and BarImpl - coding to interface isn't feasible here.
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!