3

我有超抽象类 Node 和 50 种类型的子类 SubNode。

我有一个通用类 <E extends Node> 它有一个私有 var List<E> 和一个不幸的是必须接受超类 Node ALWAYS 的方法,不能将它移动到 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 而不是由于类型擦除而添加任何对象??...

我不想为任何类型的子类编写相同的函数:

 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 (Node node); 

那么你唯一的选择是使用

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

如果没有收到警告(您当然可以禁止),就无法实现这一目标。

只要您是唯一使用此方法的人,这很好,只要您确保始终使用正确的参数调用它,您就可以忽略警告。

于 2012-07-23T10:18:42.600 回答
2

你的设计有缺陷。方法的签名应该是:

public void addSubElement (E node)

或者subElements应该是List<Node>type 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 回答
1

使用泛型时,在某些极端情况下,您无法在不抑制警告的情况下编写有效代码。

addSubElement()解决您的问题的纯 OO 方法是为每种类型编写方法。这将为每种类型(N * N)中的每种类型提供一种方法。您可以在各自的类型中添加特殊情况。

显然,对于任何大量不同类型(比如三个以上),方法的数量都会迅速增加,您会发现自己处于必须剪切和粘贴大量代码的境地。

即使您只需要编写其中的几个并将大部分工作委托给一个泛型addSubElement(Node)方法,它仍然会产生技术债务,因为您需要为每个新类型编写 X 个方法。

因此,对于您的极端情况,可能无法解决instanceofand @SuppressWarnings("unchecked")

[编辑]

您可以定义一个INode必须Node实现的接口。你的班级可能看起来像这样:

public Node<E,N extends INode> {

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

此模式将允许您限制此实现接受的可能节点类型,或Node用于N接受任何内容以及您在方法中进行特殊处理的地方。

这里的优点是,当您将类型传递给无法处理它的实现时,您可能会遇到编译时错误。但是您仍然需要在方法中进行强制转换并抑制警告。

于 2012-07-23T11:37:39.673 回答
0

编辑:所有其他答案的最终答案:

我在犹豫是否接受 titofb 的回答,因为他是指定好方法的人。但我认为它足以针对应该接受我自己的问题(第一次),以造福于阅读它的其他人。谢谢大家!

抽象节点类实现了这一点:

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

所以任何子节点总是有一个可用的方法,它从定义 SubnodeN 中返回他自己的类型 E 扩展 Node<SubNodeX> (SubnodeX = N in Node<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 回答