2013-08-07 1 views
1

Я хочу убедиться, что это будет работать до того, как мой код станет слишком большим и сложным для публикации. Мне не хватает, чтобы проверить, работает ли это так, как я ожидаю.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(), ссылаясь на то, что я использую фактический класс подкласса Узла, чтобы получить надлежащий перегруженный метод в Посетителе ,

Мое понимание этого правильно или я что-то упускаю?

ответ

1

Я не уверен, что это сработает, если вы перейдете в Object.class как тип параметра, но я вижу еще одну потенциальную проблему: Если ваши новые узлы имеют частный, а не публичный метод «посещения», то вам следует учитывать это в исключении, например:

try{ 
      Method m = v.getClass().getMethod("visit", getClass(),Object.class); 
      return (R)m.invoke(v, this,p); 
    } catch (NoSuchMethodException e) { 
     try { 
      Method m = v.getClass().getDeclaredMethod("visit", getClass(),Object.class); 
      return (R)m.invoke(v, this,p); 
      } catch (Exception e){ 
      return (R)v.visit(this,p);//default 
      } 
     } catch (Exception e){ 
      return (R)v.visit(this,p);//default 
     } 

+0

Они должны иметь открытый метод, поскольку они будут реализовывать интерфейс Visitor. Кроме того, 'Exception e', вероятно, не самый лучший способ поймать по умолчанию. (редактирование OP, чтобы отразить это) – MatrixPeckham