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