我有超级抽象类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
}
答案 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
}