也许我在这里误解了什么- 下降票会显示- 但是...
这听起来像使用Covariance的教科书范例。
当你有一个类Edge
,它具有方法Node getNode()
,那么你可以定义一个更具体的返回类型,该类型在extends Edge
的类中定义。例如:
class Node {}
class Edge {
Node getNode();
}
class GraphicalNode extends Node {}
class GraphicalEdge extends Edge {
@Override
GraphicalNode getNode();
}
或者,使用您提供的类,扩展了您提到的构造函数和一些getter方法,组合成一个MCVE:
public class WhatIsCovariance
{
public static void main(String[] args)
{
Node n0 = new Node();
Node n1 = new Node();
Edge e0 = new Edge(n0, n1);
Node n = e0.getNode0();
GraphicalNode gn0 = new GraphicalNode();
GraphicalNode gn1 = new GraphicalNode();
GraphicalEdge ge0 = new GraphicalEdge(gn0, gn1);
GraphicalNode gn = ge0.getNode0();
}
}
class Node
{
private String label;
}
class Edge
{
private Node node0;
private Node node1;
Edge(Node node0, Node node1)
{
this.node0 = node0;
this.node1 = node1;
}
public Node getNode0()
{
return node0;
}
public Node getNode1()
{
return node1;
}
}
class GraphicalNode extends Node
{
private int x;
private int y;
}
class GraphicalEdge extends Edge
{
private GraphicalNode node0;
private GraphicalNode node1;
GraphicalEdge(GraphicalNode node0, GraphicalNode node1)
{
super(node0, node1);
this.node0 = node0;
this.node1 = node1;
}
@Override
public GraphicalNode getNode0()
{
return node0;
}
@Override
public GraphicalNode getNode1()
{
return node1;
}
}
关键点在于:当你拥有一个类型为
Edge
的引用时,你只能从中获取一个
Node
(即使该引用所指的对象实际上是一个
GraphicalEdge
)。只有当引用的类型是
GraphicalEdge
时,你才能从中获取一个
GraphicalNode
。
这在许多情况下非常方便,并且通常允许干净地分离关注点:当一个方法只需要操作Edge
和Node
对象,并且不关心它们的图形表示时,你可以使用基类编写其签名:
void computeSomething(Edge edge) {
Node n0 = edge.getNode();
Node n1 = edge.getNode();
...
}
void run() {
GraphicialEdge e = new GraphicalEdge(...);
computeSomething(e);
}
当一个方法确实需要图形表示时,你让它拥有图形优势。
void drawSomething(GraphicalEdge edge) {
GraphicalNode n0 = edge.getNode();
GraphicalNode n1 = edge.getNode();
...
}
void run() {
GraphicialEdge e = new GraphicalEdge(...);
computeSomething(e);
drawSomething(e);
Edge edge = e;
drawSomething(edge);
}
一则旁注,或许是关键点,考虑到你的问题特别涉及...
类变量:
根据你所设计的当前结构,每个GraphicalEdge
将会存储其节点两次——一次作为GraphicalNode
,另一次作为简单的Node
。可以通过定义接口来避免这种情况:
interface Node {}
interface Edge {
Node getNode();
}
interface GraphicalNode extends Node {}
interface GraphicalEdge extends Edge {
@Override
GraphicalNode getNode();
}
class DefaultEdge implements GraphicalEdge { ... }
使用泛型,如
wero在他的回答中建议的那样,可能会增加一些更多的自由度,也许可以实现更清晰和更灵活的设计,以便进一步处理
Node
类型。例如,您可能希望稍后引入类似于
ColoredGraphicalNode
之类的东西,而这可以很好地被泛型类型参数所覆盖。但是,这将付出一些更加难以理解的方法签名的代价:编写通用形式的方法,允许“路由”所需的类型信息可能会变得有点麻烦,具体取决于您想要到达哪个程度。
GraphicalNode
是一个Node
,所以你可以将它存储在Edge
的成员中,并添加两个 getter 到GraphicalEdge
中,当访问GraphicalNode
时封装转换。 - Andreas Fester