This Scala tutorial让我感到困惑; Node
抽象类型似乎不遵循传统的多态性规则......
type Node <: NodeIntf // NodeIntf is assignable to Node.
abstract class NodeIntf {
def connectWith(node: Node): Edge
}
class NodeImpl extends NodeIntf {
def connectWith(node: Node): Edge = {
val edge = newEdge(this, node) // NodeImpl (this) is assignable to NodeIntf.
edges = edge :: edges
edge
}
}
protected def newEdge(from: Node, to: Node): Edge
如果Node = NodeIntf
和NodeIntf = NodeImpl
,我们为什么不能Node = NodeImpl
?我问,因为显然上面的代码不会编译 - 为什么必须使用'自键型引用'? (see tutorial)
答案 0 :(得分:7)
你颠倒了<:
的含义。 Node
可分配给NodeIntf
,即:
val x: NodeIntf = y: Node
现在,在下面你说Node = NodeIntf
和NodeIntf = NodeImpl
,这不是真的。 Node
是NodeIntf
的任意子类型,NodeImpl
是NodeIntf
的特定子类型。
就是而言,Node
是NodeIntf
,而NodeImpl
是NodeIntf
,但对此没有意义他们之间的关系 - 您可能也说Node
和NodeImpl
都是Any
的子类型。
答案 1 :(得分:3)
首先,这是一个代码的最小,自包含版本:
abstract class Graph {
type Node <: NodeIntf
case class Edge(s: Node, d: Node)
abstract class NodeIntf {
def connectWith(node: Node): Edge
}
class NodeImpl extends NodeIntf {
def connectWith(node: Node): Edge = {
val edge = newEdge(this, node)
edge
}
}
def newEdge(from: Node, to: Node): Edge = Edge(from, to)
}
如果您尝试编译它,您将获得
found : NodeImpl.this.type (with underlying type Graph.this.NodeImpl)
required: Graph.this.Node
val edge = newEdge(this, node)
^
错误消息的原因是Node
是抽象类型。它有上限NodeIntf
,但它仍然是抽象的。也就是说,抽象Graph
的实现可以自由地设置/绑定Node
到 NodeIntf
的任何子类型。
在您的代码中,您尝试将NodeImpl
的实例传递给newEdge
,该实例需要Node
。你是对的NodeImpl
是[{1}}的子类型,但NodeIntf
的实现可能决定通过将Graph
绑定到Node
的子类型来进一步限制NodeImpl
1}},这会使您对newEdge
的调用非法。
如果您已绑定Node
,例如,将其设为NodeIntf
的类型别名,
type Node = NodeIntf
然后上面的代码编译,因为Graph
的后续实现不再绑定Node
。