2

そのため、Java 継承の Fluent メソッドの戻り値の型で説明されている解決策に従って、親の型ではなく、インシデント クラスの型を返します。複数のレベルに拡張したい。

ソリューションは明らかに 1 つのレベルで機能します。コンパイル済みで実行可能なコード (依存関係なし) は次のとおりです。

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

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

    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 class FooImpl<T> extends AbstractFooBase<T, Foo<T>> implements Foo<T> {
        @Override
        void internalFoo() {
            System.out.println("inside FooImpl::internalFoo()");
        }

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

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

ただし、オブジェクトの継承階層に新しいレベルを追加すると、事態が難しくなります。以下のコードはコンパイルされません。

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

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

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

    static interface Bar<T> extends BaseBar<T, Bar<T>> {
        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 class FooImpl<T> extends AbstractFooBase<T, Foo<T>> implements Foo<T> {
        @Override
        void internalFoo() {
            System.out.println("inside FooImpl::internalFoo()");
        }

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

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

    static class BarImpl<T> extends AbstractBarBase<T, Bar<T>> implements Bar<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) {
        Foo<String> foo = new FooImpl<String>();
        foo.foo().foo1();

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

コンパイル時のエラー メッセージは次のとおりです。

X.java:40: X.BaseFoo cannot be inherited with different arguments: <T,S> and <T,X.Foo<T>>
    static abstract class AbstractBarBase<T, S extends BaseBar<T, S>> extends FooImpl<T> implements BaseBar<T, S> {
                    ^
X.java:49: X.BaseFoo cannot be inherited with different arguments: <T,X.Bar<T>> and <T,X.Foo<T>>
    static class BarImpl<T> extends AbstractBarBase<T, Bar<T>> implements Bar<T> {
           ^
Note: X.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
2 errors

それを回避する方法はありますか?

4

1 に答える 1

2

これは継承階層です。

継承階層

ご覧のとおり、これらのタイプのいくつかは、同じインターフェイス タイプを複数回継承しています。実際、は 4 回BarImpl実装されBaseFooており、継承チェーンの一部はその型パラメーターに異なる引数を提供していますSBarImpl以下を実装していると言えます。

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

同じインターフェイスを異なる型引数で実装することはできないため、コンパイラ エラーが発生します。

フォローアップの質問で指摘したように、ここでの私の回答では、「自己型」を適切にエミュレートして、あなたがやろうとしているような階層的な流暢なビルダー パターンを実装する方法について説明します。Sその中で、すべての中間型で変数「自己型」(コード内) を維持する必要があることを指摘していますfinal。中間型FooBar、および FooImplが時期尚早に解決されているため、コードはその規則に違反していSます。

次の解決策は、問題を解決します。

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

私の変更点は次のとおりです。

  • 維持SするFoo
  • 維持SするBar
  • 次のように分割FooImplします。
    • AbstractIntermediateFooでありabstract、 と を維持しS、実装internalFoofoo1ます。
    • FooImpl、これは具体的でfinalあり、解決しSます。
  • 作るBarImpl final
  • ここでは、インターフェイスへのmainコーディングfoobar実行できません。FooImplBarImpl
于 2013-10-19T22:38:04.097 に答える