Как только вы забыли реальный тип объекта, передав его в качестве одного из его супертипов, нет безопасного способа вернуть его обратно, потому что:
экземпляр subtype - это экземпляр supertype
но
экземпляр супертипа не является экземпляром подтипа
логично, что вы не можете вернуться от экземпляра супертипа к экземпляру подтипа - по крайней мере, без какой-либо гарантии безопасности.
Единственный способ обойти это - помнить подтип, когда вам нужно выполнять операции с подтипами. Если ваш язык поддерживает их, ковариантные типы возвращаемых данных можно использовать для улучшения определений интерфейса, чтобы подтип не терялся без необходимости:
In ITree:
public function getParent():ITree
In TreeNode:
public function getParent():TreeNode
Таким образом, когда вы вызываете getParent () для TreeNode, вы получаете TreeNode, а не ITree. Конечно, если вы вызываете getParent () точно в том же экземпляре, объявленном как ITree, вы получаете обратно 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);
}
}