1

オブジェクトのツリーを生成する Java で小さな内部 DSL を作成しようとしています。DSL コードは次のようになります。

RuleBuilder builder = new RuleBuilder(new Syntax());
Syntax s =
builder.rule("rule1")
            .identifier("foo")
            .choice()
                .terminal("bar")
            .end() // 1) Here it works.
       .end() // 2) Here complains the compiler.
       .rule("rule2")
            .identifier("bar")
       .end()
       .build();

コンパイラは (2 で)java.lang.Objectによって返されたオブジェクトにend()メソッドがないと文句を言いますrule()。Object にこのメソッドがないことは明らかです。ビルダー コードは次のとおりです (ツリーを組み立てるコードは簡単にするために残されています)。

class RuleBuilder {
    private final Syntax syntax;

    public RuleBuilder(Syntax syntax) {
        this.syntax = syntax;
    }

    public GenericBuilder<RuleBuilder> rule(String name) {
        return new GenericBuilder<RuleBuilder>(this);
    }

    public Syntax build() { return syntax; }
}

class GenericBuilder<P> {
    private final P parentBuilder;

    public GenericBuilder(P parentBuilder) {
        this.parentBuilder = parentBuilder;
    }

    public <P> P end() {
        return (P)parentBuilder;
    }

    public GenericBuilder<P> identifier(String value) { return this; }
    public GenericBuilder<P> terminal(String value) { return this; }
    public GenericBuilder<GenericBuilder> choice() { return new GenericBuilder<GenericBuilder>(this); }
    // ... other sub node types
}           

私の実装の背後にある主なアイデア: 生成された構文ツリーは、いくつかのルール ノードを持つ構文ルート ノードで構成されます。このルール ノードには、いくつかのサブノードを含めることができます。サブノードには、リーフとノードの 2 つのタイプがあります (コード例では、簡単にするために選択のみを行っています)。ノードとリーフをツリーに追加できる流暢なインターフェースを提供します。ツリー ブランチを「終了」するにend()は、親ビルダーを返すメソッドがあります。

私が解決しようとしている問題は、メソッドend()が親ビルダー オブジェクトを返す必要があることRuleBuilderですGenericBuilder。上記の例で、1) で機能し、2) で機能しない理由がわかりません。

List<T>ジェネリックに関する多くのリソースを読み、どのように機能するかを理解してMap<K,V>います。そして、実行時に型情報が失われるという「消去」のことを認識しています。したがって、型情報が消去されると、実行時end()に が返されることを理解できます。java.lang.Objectしかし、コンパイル時エラーが発生しました。Super Type Tokens と Typesafe Heterogenous Containers に関する Neal Gafter のブログも読みました。しかし、これで問題が解決するかどうかはわかりません。(Web での検索中に) いくつかの異なるアプローチを試みましたが、今は行き詰っています。

4

1 に答える 1

4

2 つの問題があります。まずこれがあります:

public GenericBuilder<GenericBuilder> choice() { 
    return new GenericBuilder<GenericBuilder>(this);
}

それは、「内側」がどのようなものかを言っているのではありません。GenericBuilder次のように変更できるはずです。

public GenericBuilder<GenericBuilder<P>> choice() { 
    return new GenericBuilder<GenericBuilder<P>>(this);
}

その時点で、内部end()呼び出しはGenericBuilder<RuleBuilder>raw の代わりに を持っていることを認識しGenericBuilderます。

2番目の問題は次のとおりです。

public <P> P end() {
    return (P)parentBuilder;
}

これはジェネリック メソッドであってはならない場合です。ここでは新しい型パラメーターを導入したくありません。P既存のものだけが必要です:

public P end() {
    return parentBuilder;
}
于 2012-04-23T15:39:42.953 に答える