1
1
---
2
2
layout : tour
3
- title : Inner Classes
3
+ title : 内部类
4
4
5
5
discourse : false
6
6
@@ -13,3 +13,71 @@ language: zh-cn
13
13
next-page : abstract-types
14
14
previous-page : lower-type-bounds
15
15
---
16
+
17
+ 在Scala中,一个类可以作为另一个类的成员。 在一些类似 Java 的语言中,内部类是外部类的成员,而 Scala 正好相反,内部类是绑定到外部对象的。 假设我们希望编译器在编译时阻止我们混淆节点 nodes 与图形 graph 的关系,路径依赖类型提供了一种解决方案。
18
+
19
+ 为了说明差异,我们简单描述了一个图形数据类型的实现:
20
+
21
+ ``` tut
22
+ class Graph {
23
+ class Node {
24
+ var connectedNodes: List[Node] = Nil
25
+ def connectTo(node: Node) {
26
+ if (connectedNodes.find(node.equals).isEmpty) {
27
+ connectedNodes = node :: connectedNodes
28
+ }
29
+ }
30
+ }
31
+ var nodes: List[Node] = Nil
32
+ def newNode: Node = {
33
+ val res = new Node
34
+ nodes = res :: nodes
35
+ res
36
+ }
37
+ }
38
+ ```
39
+ 该程序将图形表示为节点列表 (` List[Node] ` )。 每个节点都有一个用来存储与其相连的其他节点的列表 (` connectedNodes ` )。 类 ` Node ` 是一个 _ 路径依赖类型_ ,因为它嵌套在类 ` Graph ` 中。 因此,` connectedNodes ` 中存储的所有节点必须使用同一个 ` Graph ` 的实例对象的 ` newNode ` 方法来创建。
40
+
41
+ ``` tut
42
+ val graph1: Graph = new Graph
43
+ val node1: graph1.Node = graph1.newNode
44
+ val node2: graph1.Node = graph1.newNode
45
+ val node3: graph1.Node = graph1.newNode
46
+ node1.connectTo(node2)
47
+ node3.connectTo(node1)
48
+ ```
49
+ 为清楚起见,我们已经明确地将 ` node1 ` ,` node2 ` ,和 ` node3 ` 的类型声明为` graph1.Node ` ,但编译器其实可以自动推断出它。 这是因为当我们通过调用 ` graph1.newNode ` 来调用 ` new Node ` 时,该方法产生特定于实例 ` graph1 ` 的 ` Node ` 类型的实例对象。
50
+
51
+ 如果我们现在有两个图形,Scala 的类型系统不允许我们将一个图形中定义的节点与另一个图形的节点混合,因为另一个图形的节点具有不同的类型。
52
+ 下例是一个非法的程序:
53
+
54
+ ```
55
+ val graph1: Graph = new Graph
56
+ val node1: graph1.Node = graph1.newNode
57
+ val node2: graph1.Node = graph1.newNode
58
+ node1.connectTo(node2) // legal
59
+ val graph2: Graph = new Graph
60
+ val node3: graph2.Node = graph2.newNode
61
+ node1.connectTo(node3) // illegal!
62
+ ```
63
+ 类型 ` graph1.Node ` 与类型 ` graph2.Node ` 完全不同。 在 Java 中,上一个示例程序中的最后一行是正确的。 对于两个图形的节点,Java 将分配相同的类型 ` Graph.Node ` ; 即 ` Node ` 以类 ` Graph ` 为前缀。 在Scala中也可以表示出这种类型,它写成了 ` Graph#Node ` 。 如果我们希望能够连接不同图形的节点,我们必须通过以下方式更改图形类的初始实现的定义:
64
+
65
+ ``` tut
66
+ class Graph {
67
+ class Node {
68
+ var connectedNodes: List[Graph#Node] = Nil
69
+ def connectTo(node: Graph#Node) {
70
+ if (connectedNodes.find(node.equals).isEmpty) {
71
+ connectedNodes = node :: connectedNodes
72
+ }
73
+ }
74
+ }
75
+ var nodes: List[Node] = Nil
76
+ def newNode: Node = {
77
+ val res = new Node
78
+ nodes = res :: nodes
79
+ res
80
+ }
81
+ }
82
+ ```
83
+
0 commit comments