Я хочу убедиться, что это будет работать до того, как мой код станет слишком большим и сложным для публикации. Мне не хватает, чтобы проверить, работает ли это так, как я ожидаю.Java Generics with Reflection for Visitor Pattern
Я работаю над тем, что я хотел бы использовать шаблон посетителя в AST. Моя цель состоит в том, чтобы убедиться, что посетитель почти прозрачен при внедрении нового типа TreeNode, устраняя необходимость переопределять accept(Visitor)
в каждом подклассе, используя отражение в суперклассе.
Позволяя visit(TreeNode)
, он позволяет использовать метод по умолчанию для неизвестных типов узлов, поэтому старые пользователи не нуждаются в изменении при добавлении нового типа узла.
Класс Параметр R и P - это возвращаемое значение и параметр для посещения, трюк, который я взял у этого programming stack exchange question.
Для этого я следующее:
public abstract class TreeNode {
public final < R, P > R accept(TreeVisitor<R,P> v, P p){
try{
Method m = v.getClass().getMethod("visit", getClass(),Object.class);
return (R)m.invoke(v, this,p);
} catch (IllegalAccessException ex) {
} catch (IllegalArgumentException ex) {
} catch (InvocationTargetException ex) {
} catch (NoSuchMethodException nsme){
}
return (R)v.visit(this,p);
}
public abstract void contains(TreeNode n);//and other methods
}
//in another file
interface TreeVisitor<R,P> {
public R visit(TreeNode n,P p);//default
public R visit(TreeNodeSubclass tns,P p);
//all other subclasses as well
}
//from here lower is un-tested, written just now, just for this post, code
//somewhere else we have an algorithm to visit nodes
class DoStuff implements TreeVisitor<String,Void>{
public String visit(TreeNode n, Void v){
return n.toString();
}
public String visit(TreeNodeSubclass n, Void v){
return "SUB:" + n.toString();
}
}
//algorithm in a method somewhere
DoStuff ds = new DoStuff();
for(TreeNode node : inOrderTraverse(ROOT_NODE)){
node.accept(ds);
}
Будет ли это работать как я ожидать (при условии, inOrderTraverse(ROOT_NODE)
выдает список всех узлов должным образом)?
Мой главный вопрос на самом деле часть с getMethod
вызова, из-за типа стирания Object.class
должен быть правильный параметр, даже если один предпочел бы использовать p.getClass()
из родового параметра P
. Это, однако, не сработало бы, потому что стирание стилей заставляет фактическую подпись метода в Visitor быть Object visit(this.getClass(), Object)
, this.getClass()
, ссылаясь на то, что я использую фактический класс подкласса Узла, чтобы получить надлежащий перегруженный метод в Посетителе ,
Мое понимание этого правильно или я что-то упускаю?
Они должны иметь открытый метод, поскольку они будут реализовывать интерфейс Visitor. Кроме того, 'Exception e', вероятно, не самый лучший способ поймать по умолчанию. (редактирование OP, чтобы отразить это) – MatrixPeckham