我有一个有趣的情况,想知道是否有更好的方法来解决。情况是这样的,我有一个树形结构(具体来说是抽象语法树),某些节点可以包含各种类型的子节点,但都继承自给定的基类。
我经常需要在这个树上进行查询,并且希望得到我感兴趣的特定子类型的结果。因此,我创建了一个谓词类,然后将其传递给一个通用的查询方法。起初,我的查询方法看起来像这样:
这里的重点是,谓词接口扩展为提供
我经常需要在这个树上进行查询,并且希望得到我感兴趣的特定子类型的结果。因此,我创建了一个谓词类,然后将其传递给一个通用的查询方法。起初,我的查询方法看起来像这样:
public <T extends Element> List<T> findAll(IElementPredicate pred, Class<T> c);
在这里,Class
参数只是用来指示返回类型。我对这种方法感到不满的原因是我的所有谓词已经针对特定类型了,所以这里存在冗余信息。一个典型的调用可能如下所示:
List<Declaration> decls =
scope.findAll(new DeclarationPredicate(), Declaration.class);
所以我像这样重构了它:
public <T extends Element> List<T> findAll(IElementPredicate<T> pred);
IElementPredicate
接口的样式如下:
public interface IElementPredicate<T extends Element> {
public boolean match(T e);
public String getDescription();
public Class<T> getGenericClass();
}
这里的重点是,谓词接口扩展为提供
Class
对象。这使得编写实际的findAll
方法需要更多的工作,并且在编写谓词时增加了一些工作,但这两个都基本上是微小的“一次性”任务,因为它使查询调用变得更加友好,因为您不必添加额外的(可能是冗余的)参数,例如。List<Declaration> decls = scope.findAll(new DeclarationPredicate());
我以前没有注意到这种模式。这是处理Java泛型语义的典型方式吗?只是好奇我是否错过了更好的模式。
有评论吗?
更新:
一个问题是你需要类(Class)做什么?下面是findAll的实现:
public <T extends Element> List<T> findAll(IElementPredicate<T> pred) {
List<T> ret = new LinkedList<T>();
Class<T> c = pred.getGenericClass();
for(Element e: elements) {
if (!c.isInstance(e)) continue;
T obj = c.cast(e);
if (pred.match(obj)) {
ret.add(c.cast(e));
}
}
return ret;
}
虽然匹配只需要一个T,但我需要确保对象是T类型才能调用它。为了做到这一点,我需要Class的“isInstance”和“cast”方法(据我所知)。