在 Scala 中,让类中有其它类作为成员是可能的。与类 Java 的语言内部类作为封闭类的成员相反,,在 Scala 中内部类是被绑定到外部对象上的。假设编译器在编译时想阻止我混合哪些结点属于哪些类。路径依赖类型提供了一个解决方案。
为了演示不同, 我们迅速实现一个图的数据类型:
class Graph {
class Node {
var connectedNodes: List[Node] = Nil
def connectTo(node: Node) {
if (connectedNodes.find(node.equals).isEmpty) {
connectedNodes = node :: connectedNodes
}
}
}
var nodes: List[Node] = Nil
def newNode: Node = {
val res = new Node
nodes = res :: nodes
res
}
}
这个程序代表了一个拥有列表结点(List[Node]
)的图。每个结点
val graph1: Graph = new Graph
val node1: graph1.Node = graph1.newNode
val node2: graph1.Node = graph1.newNode
val node3: graph1.Node = graph1.newNode
node1.connectTo(node2)
node2.connectTo(node3)
我们明确地声明了 node1
, node2
, node3
的类型为 graph1.Node
,但是实际上编译器可以推断出它。这是因为当我们调用 graph1.newNode
时调用了 new Node
,这个方法使用了特定实例 garph1
的 Node
的实例。
如果我们有两个图的实例,Scala 的类型系统不会允许我们把一个实例图中的定义的结点混合到另一个实例图中定义的结点中。因为另一个实例图中的结点类型是另一种类型。这里有一个错误的程序例子:
val graph1: Graph = new Graph
val node1: graph1.Node = graph1.newNode
val node2: graph1.Node = graph1.newNode
node1.connectTo(node2) // 合法
val graph2: Graph = new Graph
val node3: graph2.Node = graph2.newNode
node1.connectTo(node3) // 非法
graph1.Node
与 grpah2.Node
类型是不同的。在 Java 中上面的例子中的最后一行是对的。图中结点的类型都是相同的 Graph.Node
类型。在 Scala 中像这样的类型被表示为 Graph#Node
。如果我们想让不同的图中的结点一起使用,可以改变在图中的定义像下面的例子进行实现:
class Graph {
class Node {
var connectedNodes: List[Graph#Node] = Nil
def connectTo(node: Graph#Node) {
if (connectedNodes.find(node.equals).isEmpty) {
connectedNodes = node :: connectedNodes
}
}
}
var nodes: List[Node] = Nil
def newNode: Node = {
val res = new Node
nodes = res :: nodes
res
}
}
注意:内部类不允许我们将两个图中的结点一起使用,如果我们想解除这个限制,我们需要改变结点变量的类型为
Grpah#Node
。