3

超抽象クラス Node と 50 種類のサブクラス SubNode があります。

プライベート var List<E> を持つジェネリック Class <E extends Node> と、残念ながらスーパークラス Node を常に受け​​入れなければならないメソッドがあり、E だけに移動することはできません。

 public void addSubElement (Node node){ 
        if (node instanceOf E) subElements.add((E)node); 
        else //Doing extra steps for occasional non-E nodes like discarding them silently without CastException; 
 }

タイプ消去のためにオブジェクトを追加する代わりに CastException をスローして、警告なしでコンパイルできるソリューション (Reflection?) はありますか??...

どのタイプのサブクラスに対しても同じ関数を作成する必要はありません。

 public void addSubElement (Node node){                             
        if (node instanceOf SubNode1) subElements.add((SubNode1)node); 
        if (node instanceOf SubNode2) subElements.add((SubNode2)node); 
        //if (node instanceOf SubNode50....
 }

のような方法があればとてもいいです。

public void addSubElement (Node node){ 
        subElements.add((E)node.autoCastToSubClassOfAbstract("Node")); //Should throw CastException if non-E
 }

また

 public void addSubElement (Node node){ 
        subElements.add(node.autoCastTo("E")); //Should throw CastException if non-E
 }
4

4 に答える 4

2

デザインに欠陥があります。メソッドのシグネチャは次のようになります。

public void addSubElement (E node)

または、代わりsubElementsにタイプにする必要があります。List<Node>List<E>

クラスがであるとしましょう。NodeList<E extends Node>次に、インスタンスを作成します。

NodeList<SubNode1> nl = new NodeList<SubNode1>();

その場合、リストはのインスタンスのみを受け入れるSubNode1ため、それを行うことはできません。

nl.addSubElement(subNode2Instance)

アップデート:

私が見つけた唯一の回避策はこれです:

private static class G<E extends NodeB> {

    private E templateObject;

    private List<E> subElements = new ArrayList<E>();

    public G(E templateObject) {
        this.templateObject = templateObject;
    }

    public void addSubElement (NodeB node) {
        if (templateObject.getClass().isAssignableFrom(node.getClass())) {
            subElements.add((E) node);
        } else {
            throw new ClassCastException();
        }
    }

}
于 2012-07-23T10:29:51.497 に答える
2

何らかの形 で強制された場合

public void addSubElement (Node node); 

あなたの唯一のオプションは使用することです

public void addSubElement (Node node){ 
    subElements.add((E)node); 
}

警告を受けずにこれを達成する方法はありません (もちろん抑制できます)。

これは、このメソッドを使用している唯一の人である限り問題ありません。常に正しい引数で呼び出すことを確認している限り、警告を無視できます。

于 2012-07-23T10:18:42.600 に答える
1

When using generics, there are corner cases where you can't write valid code without suppressing warnings.

The pure OO approach for your problem would be to write on addSubElement() method for each type. That would give you one method per type in each type (N*N). You could add the special cases in the respective types.

Obviously for any significant number of different types (say more than three), the number of methods quickly explodes and you will find yourself in a situation where you have to cut&paste a lot of code.

Even if you need only to write a few of them and delegate most of the work to a generic addSubElement(Node) method, it would still create a technical debt because you'd need to write X methods for each new type.

So for your corner case, there is probably no way around instanceof and @SuppressWarnings("unchecked").

[EDIT]

You could define an interface INode which Node has to implement. Your class could then look like this:

public Node<E,N extends INode> {

    @SuppressWarnings("unchecked")
    public void addSubElement(N n) { ... }
}

This pattern would allow you to either restrict the possible types of nodes which this implementation accepts or use Node for N for something that accepts anything and where you do special handling in the method.

The advantage here is that you could get compile time errors when you pass a type to an implementation which can't handle it. But you would still need a cast in the method and to suppress the warning.

于 2012-07-23T11:37:39.673 に答える
0

編集:他のすべての回答からの最終回答:

私はtitobの回答を受け入れるかどうかためらっていました. 彼は良い方法を指定した人だからです. しかし、それを読む他の人の利益のために、私自身の問題を(非常に初めて)受け入れる必要があるのは、yの問題に的を絞ったことで十分だと思います。ありがとうございます!

Abstract Node クラスはこれを実装します。

    @SuppressWarnings("unchecked")
    protected final Class<E> getChildType(){
        return (Class<E>)(((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0]); 
    }

したがって、任意のサブノードには、定義 SubnodeN extends Node<SubNodeX> (SubnodeX = N in Node<E>) から独自のタイプ E を返す使用可能なメソッドが常にあります。つまり、どのノードでも実行できます。

 public void addSubElement (Node<?> node){ 
      Class<E> expectedChildType = getChildType();
      if (expectedChildType.isAssignableFrom(node.getClass())){//if node instanceOf E
         subElements.add(expectedChildType.cast(node)); 
      }
      else throw new ClassCastException("A non-expected child was intended to add to this "+this.getClass().getSimpleName()+" element");
 }

そして、ここで魔法です。これがデフォルトの動作です。一部の子が予期されていない場合は警告が表示されますが、特殊なケースを処理するために、任意のサブノードに対してこのメ​​ソッドをオーバーライドできます。

 @Override
 public void addSubElement (Node<?> node){ 
      if (node instanceOf SubNode34) {/* Yes, our current subnode does not expect all elements intended to be added as E type nodes, we can silently discard or do whatever */}
      else super.addSubElement(node) //Parents default behavior
 }    
于 2012-07-23T14:14:08.693 に答える