2013-02-15 124 views
2

This Scala tutorial令我困惑;在Node抽象类型似乎并不以下多态性的传统规则...Scala抽象类型和多态性

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 = NodeIntfNodeIntf = NodeImpl,那么我们为什么不能做Node = NodeImpl?我问,因为显然上面的代码不会编译 - 为什么必须使用'自我类型引用'? (see tutorial

回答

7

您倒置了<:的含义。一个Node被分配给一个NodeIntf,那就是:

val x: NodeIntf = y: Node 

现在,下面将进一步你说Node = NodeIntfNodeIntf = NodeImpl,这是不正确的。 Node任意子类型NodeIntfNodeImplNodeIntf的特定子类型。

而言是一个NodeNodeIntf,并NodeImplNodeIntf,但对它们之间的关系没有任何意义 - 你还不如说,无论NodeNodeImpl是两种亚型Any

3

首先,这里是一个排序的代码的最小的,自包含的版本:

​​

如果你尝试编译它,你会得到

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。你是对的,因为NodeImplNodeIntf的子类型,但是,Graph的实现可能会决定通过将Node绑定到NodeImpl的子类型来进一步限制它,这将使您致电newEdge非法。

如果已经结合Node,例如,通过使其成为NodeIntf一个类型别名,

type Node = NodeIntf 

那么以上代码编译因为稍后的Graph实现不能结合Node了。