代码之家  ›  专栏  ›  技术社区  ›  itarato Randy Sugianto 'Yuku'

如果可以,我们应该使用接口方法吗?

  •  1
  • itarato Randy Sugianto 'Yuku'  · 技术社区  · 16 年前

    这个问题有点假设性。假设我有一个绘制树的应用程序。我的结构控制器实现了ITree接口,该接口描述了一个普通树,其方法如下:getChildren(),getParent()。。。应用程序的某些部分只需要知道结构实现了ITree,这样就可以查找它的子级或父级。因此getParent()的高度足以返回ITree类型的对象。因此getChildren()可以返回ITree对象的集合。

    但是:)

    铸造 将它们转换为TreeNode,以便能够调用适当的实例函数(ITree未定义)。或者我可以使用自己的迭代器遍历子节点并做任何我能做的事情(因为这样做实际上就是树节点)。

    更新:[添加插图]

    所以这里是ITree:

    public interface ITree {
      function getParent():ITree;
    }
    

    这是我的TreeNode课程:

    public class TreeNode implements ITree {
      private var parent:TreeNode
    
      public function getParent():ITree {
        return parent;
      }
    
      function foo():void {}
    
      function bar():void {
        // Version 1 - using the interface
        (getParent() as TreeNode).foo();
    
        // Version 2 - using custom object accessor
        parent.foo();
      }
    
    }
    

    哪一个更好:版本1还是版本2?

    3 回复  |  直到 16 年前
        1
  •  1
  •   Denis Palnitsky    16 年前

        2
  •  1
  •   richj    16 年前

    一旦您忘记了对象的真实类型,将其作为超级类型之一传递,就没有安全的方法将其取回,因为:

    子类实例 是一个

    但是

    超类型实例 不是一个

    因此,从逻辑上讲,您不能从超级类型实例返回到子类型实例—至少在没有任何安全保证的情况下是这样。

    解决这个问题的唯一方法是在需要执行子类型操作时记住子类型。如果您的语言支持协变返回类型,则可以使用协变返回类型来改进接口定义,这样子类型就不会不必要地丢失:

    In ITree:
        public function getParent():ITree
    
    In TreeNode:
        public function getParent():TreeNode
    

    这样,在树节点上调用getParent()时,得到的是树节点,而不是ITree。当然,如果在声明为ITree的同一个实例上调用getParent(),则返回ITree。一旦忘记了类型信息,就太晚了。

    在一个类中,你可以选择处理实类型,你仍然可以在接口中与外界对话(故意丢失超类型信息),但是实现可以处理实类型而不是接口。

    我在用Java做一些代码的实验。如果你感兴趣,这里是:

    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;
    
    import org.junit.Assert;
    import org.junit.Test;
    
    public class TestTree {
        @Test
        public void testGetDepth() {
            ITreeNode bigTree = TreeNode.create(null, 10);       
            ITreeNode left    = bigTree.getChildren().get(0);
    
            Assert.assertEquals(10, bigTree.getHeight());
            Assert.assertEquals(9,  left.getHeight());
    
            Assert.assertEquals(0, bigTree.getDepth());
            Assert.assertEquals(1, left.getDepth());
        }
    }
    
    interface ITree {
        List<? extends ITree> getChildren();
        ITree                 getParent();
    }
    
    interface ITreeNode extends ITree {
        @Override
        List<? extends ITreeNode> getChildren();
    
        int getDepth();
        int getHeight();
    }
    
    class TreeNode implements ITreeNode {
        private ITreeNode m_parent;
    
        private final TreeNode m_left;
        private final TreeNode m_right;
        private final List<ITreeNode> m_children;
    
        public static ITreeNode create(ITreeNode parent, int depth) { 
            TreeNode node = createDescendants(depth);
    
            node.setParents(parent);
    
            return node;
        }
    
        private static TreeNode createDescendants(int depth) { 
            if (depth == 0) {
                return new TreeNode(null, null, null);
            }
            else {
                return new TreeNode(null, TreeNode.createDescendants(depth - 1), TreeNode.createDescendants(depth - 1));
            }
        }
    
        private TreeNode(ITreeNode parent, TreeNode left, TreeNode right) {
            m_parent = parent;
            m_left   = left;
            m_right  = right;
    
            List<ITreeNode> children = new ArrayList<ITreeNode>();
            children.add(left);
            children.add(right);
            m_children = Collections.unmodifiableList(children);
        }
    
        private void setParents(ITreeNode parent)
        {
            m_parent = parent;
    
            if (m_left != null)
                (m_left).setParents(this);
    
            if (m_right != null)
                m_right.setParents(this);
        }
    
        public List<? extends ITreeNode> getChildren() {
            return m_children;
        }
    
        public ITreeNode getParent() {
            return m_parent;
        }
    
        public int getDepth() {
            int depth = 0;
    
            if (m_parent != null) {
                depth = m_parent.getDepth() + 1;
            }
    
            return depth;
        }
    
        public int getHeight() {
    
            int leftHeight  = (m_left == null)  ? 0 : m_left.getHeight() + 1;
            int rightHeight = (m_right == null) ? 0 : m_right.getHeight() + 1;
    
            return Math.max(leftHeight, rightHeight);
        }
    }
    
        3
  •  1
  •   itarato Randy Sugianto 'Yuku'    16 年前

    public interface ITree {
      function getParent():ITree;
    }
    

    实现ICustomTreeNode的 )然后创建一个新的ITree子接口:

    public interface ICustomTree extends ITree {
      function getCustomParent():CustomTreeNode;
    }
    

    这样我就可以使用正确类型的对象。