我怎样才能转发到班级'E型或至少在没有警告的情况下安全地进行?

时间:2012-07-23 10:08:25

标签: java generics inheritance types downcast

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

我有一个通用的Class< E extends Node>其具有私有var列表< E>并且一个不幸的是必须接受超类节点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; 
 }

任何解决方案(Reflection?)能够在没有警告的情况下编译,抛出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 个答案:

答案 0 :(得分:2)

如果你强迫以某种方式拥有

public void addSubElement (Node node); 

那么你唯一的选择就是使用

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

如果没有得到警告(你当然可以压制),就无法实现这一目标。

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

答案 1 :(得分: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();
        }
    }

}

答案 2 :(得分:1)

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

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

显然,对于任何大量不同类型(比如超过三种),方法的数量会迅速爆炸,你会发现自己处于需要剪切和粘贴大量代码的情况。

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

因此,对于您的角落案例,可能无法绕过instanceof@SuppressWarnings("unchecked")

<强> [编辑]

您可以定义INode必须实现的接口Node。您的课程可能如下所示:

public Node<E,N extends INode> {

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

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

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

答案 3 :(得分:0)

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

如果接受了titofb的答案,我会犹豫不决,因为他是指定好方法的人。但是我觉得这个问题已经足够针对我自己的问题(第一次),为了阅读它的其他人的利益。谢谢大家!

Abstract Node类实现了这个:

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

因此,任何Subnode始终都有一个可用的方法,它从定义中返回自己的Type E. SubnodeN扩展Node&lt; SubNodeX&gt; (节点&lt; E&gt;中的子节点X = N)。这意味着我们可以在任何节点中执行此操作:

 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
 }