是否可以设计一个类型安全的链表来阻止尾节点的getNext()?

时间:2012-02-06 20:05:46

标签: oop reflection data-structures types type-safety

我想知道是否有可能设计一个类型安全的单链表结构,这样就不可能从尾节点请求下一个节点。

同时,客户端需要能够通过node.getChild()遍历(递归或其他)遍历列表但在编译时(至少通过人工编写的显式类型检查)阻止进行过了尾巴。

我想知道:

  1. 这类问题有名称吗?
  2. 是否有面向对象或其他方法有助于避免显式运行时类型检查?
  3. 实现语言并不重要,但这是我正在考虑的Java示例。

    在Joop的回答后编辑:

    public class TestHiddenInterfaces {
       interface Node { HasNoChildNode getTail(); }
       interface HasNoChildNode extends Node {};
       interface HasChildNode extends Node { Node getChild(); }
       class HasNoChild implements HasNoChildNode {
          @Override public HasNoChildNode getTail() { return this; }
       }
       class HasChild implements HasChildNode {
          final Node child;
          @Override
          public Node getChild() { return child; }
          HasChild(Node child) {
             this.child = child;
          }
          @Override public HasNoChildNode getTail() {
             if(child instanceof HasChild) return ((HasChild) child).getTail();
             else if(child instanceof HasNoChild) return (HasNoChildNode) child;
             else throw new RuntimeException("Unknown type");
          }
       }
    
       @Test
       public void test() {
          HasNoChild tail = new HasNoChild();
          assertEquals(tail, tail.getTail());
          HasChild level1 = new HasChild(tail);
          assertEquals(tail, level1.getTail());
          HasChild level2 = new HasChild(level1);
          assertEquals(tail, level2.getTail());
       }
    }
    

2 个答案:

答案 0 :(得分:1)

在Scala中,人们使用“案例类型”进行此类输入。在Java或UML图中,人们经常看到分支和叶子之间的区别。这可以减少未使用的叶子儿的一半记忆。

类型与枚举值共存。

所以可以使用以下内容:

/**
 * Base of all nodes. For the remaining types I have dropped the type parameter T.
 */
public interface Node<T> {
    void setValue(T value);
    T getValue();
}

public interface HasParent extends Node {
    void setParent(HasChildren node);
    HasChildren getParent();
}

public interface HasChildren extends Node {
    void setChildren(HasParent... children);
    HasPOarent[] getChildren();
}

public final class RootBranch implements HasChildren {
    ...
}

public final class SubBranch implements HasChildren, HasParent {
    ...
}

public final class Leaf implements HasParent {
    ...
}

public final class RootLeaf implements Node {
    ...
}

用法将使用重载或区分案例:

void f(Node node) {
    if (node instanceof HasParent) {
         HasParent nodeHavingParent = (HasParent) node;
         ...
    }
}

我个人认为这在Java中过头了,但在Scala中,例如,类型声明是构造函数,这是有道理的:SubBranche(parent, child1, child2)

答案 1 :(得分:0)

这种层次结构可能存在的唯一方法是每个级别实现一个不同的接口(我在广义上使用接口而不是特定的语言术语)。

根节点无法实现getParent - 这是您实现我能想到的编译错误的唯一方法。因此,根节点的“接口”不包括getParent

第一个孩子可以实现getParent - 但是为了编译安全,他们必须返回一个类型,在编译时,它已知为根节点(即不是一种类型't 实施getParent)。

在下一个级别,getParent的实现必须返回一个实现getParent的类型,该类型返回一个没有getParent的根节点。

简而言之,即使您确实选择生成这样的实现,它也会非常脆弱,因为您需要编写不同的代码来处理层次结构的每个级别。


运行时检查 正确存在某些问题,这将是其中之一。如果每个问题都可以在编译时解决,那么每个编译的程序都只是一组结果(可能还有一个庞大的switch语句来选择你要输出的结果)