2009-05-15 14 views
10

私は興味深い状況があり、これを行うより良い方法があるのだろうかと思っています。状況はこれです、私は木構造(具体的には抽象構文木)を持っていて、いくつかのノードは様々な型の子ノードを含むことができますが、すべては与えられた基底クラスから拡張されています。 タイプの安全性、Javaのジェネリックとクエリ

は、私は頻繁にこの木にクエリをしたいと私は私が興味の特定のサブタイプを取り戻すしたいと思います。だから私は、私は、その後、一般的なクエリメソッドに渡すことができ述語クラスを作成しました。

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メソッドを少し書くだけで、述語を書くのにもう少し作業が追加されますが、それらは本質的に小さな「1回限りの」ものであり、そうしないとクエリーコールがとてもうまくなります。余分な(潜在的に冗長な)引数を追加する必要があります。

List<Declaration> decls = scope.findAll(new DeclarationPredicate()); 

私は以前にこのパターンに気付きませんでした。これはJavaジェネリックのセマンティクスを扱う典型的な方法ですか?私はいくつかのより良いパターンが欠けている場合はちょうど好奇心。

コミッション?

UPDATE:あなたは、クラスを何が必要なのですかた

一つの疑問?ここでの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であることを確認する必要があります。そのためには、クラスの "isInstance"メソッドと "cast"メソッドが必要です(私が知る限り)。

答えて

1

最も近い「パターン」はトークンタイプで、ジェネリックスチュートリアルではこれをお勧めします。ベース述部をsuper-type-token(a.k.a Gafterガジェット)に変更して、新しい述部を定義するときに余分な数の行を節約することもできます。

1

あなたがしたい場合は、明示的に建設し、キャストを回避するために、訪問者のパターン、またはバリアントを使用することができます。 the hibernate wiki page on thisを参照してください。私はあなたがあなたの問題を完全に型消去の特異性を考慮して解決するかどうかについて考えてきました。

編集:あなたの追加を見ました。述語階層をビジター階層に従わせる場合、マッチコールの前にキャストは必要ありません。同様に(テストされていない):

interface Element { 
    public boolean accept(ElementPredicateVisitor v); 
} 

class Declaration implements Element { 
    public boolean accept(ElementPredicateVisitor v) { 
     return v.visit(this); 
    }  
} 

class TaxReturn implements Element { 
    public boolean accept(ElementPredicateVisitor v) { 
     return v.visit(this); 
    }  
} 


interface IElementPredicate { 
    public void match(Element e); 
} 

class ElementPredicateVisitor implements IElementPredicate { 
    public boolean match(Element e) { 
     return e.accept(this); 
    } 
    /** 
    * default values 
    */ 
    boolean visit(Declaration d) { return false; } 
    boolean visit(TaxReturn tr) { return false; } 
} 

class DeclarationNamePredicate extends ElementPredicateVisitor { 
    boolean visit(Declaration d) { 
     return d.dSpecificExtraName() == "something" 
    } 
} 

class TaxReturnSumPredicate extends ElementPredicateVisitor { 
    boolean visit(TaxReturn tr) { 
     return tr.sum() > 1000; 
    } 
} 


public <T extends Element> List<T> findAll(IElementPredicate pred) { 
    List<T> ret = new LinkedList<T>(); 
    for(Element e: elements) {    
     if (pred.match(obj)) { 
       ret.add((T) obj); 
     } 
    } 
    return ret; 
} 
+0

私は訪問者を頻繁に使用していますが(この非常に同じプロジェクトでは、実際には)、この特定のケースでは、訪問者パターンは、必要と思われるよりもはるかに多くのコードと複雑さを追加します。 –

+0

私はあなたに同意する、それは大きな副作用がある方法でcomplicatorsスイッチのようなものです。あなたが望むように一般性を逆転するのは難しいです。私はあなたの解決策がベストであると信じています。なぜなら、ビジターがなければ、レイトバインドや明示的な型チェックが必要になるからです。あなたのソリューションは、実装者にその負担をかけるのではなく型チェックを集中させるので、より優れています。この問題は、汎用のComparable実装でも同様です。 –

+0

そして、レイトバインディングでは、Javaは参照型ではなく、オブジェクト型に基づいてオーバーロードされたメソッド解決を実装する必要があることを意味しました。これは基本的にコードで実装したものです。 –

1

ツリー構造は、XML DOMオブジェクトとよく似ています。ツリーをDOM構造に変換し、XPathを使用してクエリを実行することを検討しましたか?カスタムコードがずっと少なくなるかもしれません。

0

私はそれをやるのがいいクリーンな方法だと私はおそらく同じようにそれを行うだろうと思う。

関連する問題