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
}           

我的实现背后的主要思想:生成的语法树由一个语法根节点组成,它有一些规则节点。该规则节点可以有一些子节点。子节点有两种类型:叶子和节点(在示例代码中仅为简单选择)。我将提供一个流畅的界面,我可以在其中向树添加节点和叶子。要“结束”一个树分支,可以使用end()返回父构建器的方法。

我试图解决的问题是,该方法end()应该返回可能是类型RuleBuilderGenericBuilder. 我不明白为什么它在上面的示例中适用于 1) 而不是 2)。

我已经阅读了很多关于泛型的资源,并且我了解它们是如何List<T>工作Map<K,V>的。而且我知道“擦除”的事情,即类型信息在运行时丢失。所以我可以理解,在运行时end()返回java.lang.Object,当类型信息被擦除时。但是我得到了一个编译时错误。我还阅读了 Neal Gafter 关于 Super Type Tokens 和 Typesafe Heterogenous Containers 的博客。但我不确定这是否能解决我的问题。我尝试了几种不同的方法(在网上搜索期间),但现在我被卡住了。

4

1 回答 1

4

有两个问题。首先是这个:

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

这并不是说“内在”是什么的。GenericBuilder您应该能够将其更改为:

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

那时,内部end()调用仍然会知道它有 aGenericBuilder<RuleBuilder>而不是 raw GenericBuilder

第二个问题在这里:

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

这是一个不应该的通用方法——你不想在这里引入一个的类型参数P;你只想要现有的:

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