继承和泛型

5
我有一个应用程序,对节点和边缘G(N,E)的图执行各种分析算法。节点和边缘的属性因应用程序而异,并根据图的类型和属性的性质形成继承层次结构。例如,节点层次结构的根可以表示最一般的非定向循环图(NcgNode)。NcgNode的子类可能表示定向循环图(DcgNode),然后是DagNode等。可以应用于DAG的算法与NCG的算法不同,但反之亦然。树的根的关键行为是添加和检索图的相邻节点。问题是如何在不创建“未经检查的”异常的情况下完成此操作?
代码的简洁版本可能如下所示:
import java.util.ArrayList;
import java.util.List;

public class NcgNode {
    private List<NcgNode> nodeList_ = null;
    private List<? extends NcgNode> nodeListSrc_ = null;
    private List<? super NcgNode> nodeListSink_ = null;

    public <N extends NcgNode> void addNode(N node) {
        if (nodeList_ == null) {
            nodeList_ = new ArrayList<NcgNode>();
            nodeListSrc_ = nodeList_;
            nodeListSink_ = nodeList_;
        }
        nodeListSink_.add(node);
    }

    @SuppressWarnings("unchecked")
    // Any way to avoid this?
    public <N extends NcgNode> N getNode(int n) {
        if ((nodeList_ == null) || (n >= nodeList_.size()))
            return null;
        // causes unchecked warning:
        return (N) nodeListSrc_.get(n);
    }
}

class DcgNode extends NcgNode {
    // enables DCG algorithms, etc
}

class DagNode extends DcgNode {
    // enables DAG algorithms, etc.
}

有更好的设计方式吗?

4个回答

1

只需将您的列表类型设置为NcgNode,例如

private List<NcgNode> nodeListSrc_ = null;

您仍然可以将NcgNode的子类放入这些列表中。


1
你应该像以下这样做。在一个抽象类(NcgNode)中定义方法,参数化子节点的类型。因此,可以很容易地编写addNodegetNode。然后,你将有特定的实现(我使用了DcgNodeDagNode;不确定是否符合你的要求),作为这个抽象类的子类,其自身也是参数化的。这使得你可以拥有稍后(见下文)需要一个节点的子节点与该节点相同类型的算法。
public abstract class NcgNode<N> {
    private List<N> nodeList_ = null;

    public void addNode(N node) {
        if (nodeList_ == null) {
            nodeList_ = new ArrayList<N>();
        }
        nodeList_.add(node);
    }

    // Any way to avoid this?
    public N getNode(int n) {
        if ((nodeList_ == null) || (n >= nodeList_.size()))
            return null;
        return nodeList_.get(n);
    }
}

class DcgNode extends NcgNode<DcgNode> {
    // enables DCG algorithms, etc
}

class DagNode extends NcgNode<DagNode> {
    // enables DAG algorithms, etc.
}

//...
static <N extends NcgNode<N>> void someAlgorithm(N node) { }

你认为 DagNodeDcgNode 的子类可能不安全,因为如果一个 DagNode "是一个" DcgNode,那么这意味着你可以把任何 DcgNode 放入其中作为它的子节点,而这并不是你想要的。


如果您还想扩展DcgNode或DagNode,该怎么办? - Sarevok
如果我想要存储一个可以存储DcgNode和DagNode的List<NcgNode>,我应该如何声明它?如果我像这样声明它,编译器会显示警告,因为我使用了原始类型。 - Sarevok

0
请将您的方法修改为以下内容:
public NcgNode getNode(int n) {
  if ((nodeList_ == null) || (n >= nodeList_.size())) {
  return null;
}

return (NcgNode) nodeListSrc_.get(n);
} 

3
这个解决方案不能让调用者在不进行不安全转换的情况下使用它返回的具体子类的细节。这只是把问题往后拖延。 - Judge Mental

0

请查看“自限定类型”。(编辑:不确定为什么会有负评)

您的根类应该是抽象的,实际节点类型N应该作为类的类型参数,如下所示:

public abstract class AbstractNode< N extends AbstractNode< N > > {
    private List< N > nodeList_ = null;

    public synchronized void addNode( N node ) {
        if ( nodeList_ == null )
            nodeList_ = new ArrayList< N >();
        nodeList_.add(node);
    }

    public N getNode( int n ) {
        if ( nodeList_ == null || n >= nodeList_.size() )
            throw new NoSuchElementException();
        return nodeList_.get( n );
    }
}

具体的子类可以使用自己的类型作为N。对于深度继承层次结构,请使用另一个抽象类来保持"My Type"的活力。

class NcgNode extends AbstractNode< NcgNode > {
}

abstract class AbstractDcgNode< N extends AbstractDcgNode< N > > extends AbstractNode< N > {
    // enables DCG algorithms, etc
}

class DcgNode extends AbstractDcgNode< DcgNode > {
}

class DagNode extends AbstractDcgNode< DagNode > {
    // enables DAG algorithms, etc
}

  1. "self-bounded types" 在Java中不起作用。
  2. 如果您将AbstractNode<N extends AbstractNode<N>>替换为AbstractNode<N>,并将AbstractDcgNode<N extends AbstractDcgNode<N>>替换为AbstractDcgNode<N>,它将以相同的方式工作。
- newacct
你能更具体地说明一下评论1)吗?虽然无法强制要求类型参数限制声明它的类,但这比评论2)的建议更接近,后者允许编译的程序比我的多——也比OP所期望的多。 - Judge Mental

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接