Scala 游览

内部类

语言

在 Scala 中,可以将类作为成员让其他类使用。与 Java 类似的语言中,此类内部类是封闭类的成员,而在 Scala 中,此类内部类绑定到外部对象。假设我们希望编译器在编译时阻止我们混淆哪些节点属于哪个图。路径相关类型提供了解决方案。

为了说明差异,我们快速概述一下图数据类型的实现

class Graph {
  class Node {
    var connectedNodes: List[Node] = Nil
    def connectTo(node: Node): Unit = {
      if (!connectedNodes.exists(node.equals)) {
        connectedNodes = node :: connectedNodes
      }
    }
  }
  var nodes: List[Node] = Nil
  def newNode: Node = {
    val res = new Node
    nodes = res :: nodes
    res
  }
}
class Graph:
  class Node:
    var connectedNodes: List[Node] = Nil
    def connectTo(node: Node): Unit =
      if !connectedNodes.exists(node.equals) then
        connectedNodes = node :: connectedNodes

  var nodes: List[Node] = Nil
  def newNode: Node =
    val res = Node()
    nodes = res :: nodes
    res

此程序将图表示为节点列表 (List[Node])。每个节点都有一个连接到的其他节点列表 (connectedNodes)。class Node 是一个路径相关类型,因为它嵌套在 class Graph 中。因此,connectedNodes 中的所有节点都必须使用来自 Graph 的同一实例的 newNode 创建。

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)
node3.connectTo(node1)

我们明确声明了 node1node2node3 的类型为 graph1.Node 以提高清晰度,但编译器本可以推断出这一点。这是因为当我们调用调用 new Nodegraph1.newNode 时,该方法使用特定于实例 graph1Node 实例。

如果我们现在有两个图,Scala 的类型系统不允许我们将一个图中定义的节点与另一个图的节点混合,因为另一个图的节点具有不同的类型。这是一个非法的程序

val graph1: Graph = new Graph
val node1: graph1.Node = graph1.newNode
val node2: graph1.Node = graph1.newNode
node1.connectTo(node2)      // legal
val graph2: Graph = new Graph
val node3: graph2.Node = graph2.newNode
node1.connectTo(node3)      // illegal!

类型 graph1.Node 与类型 graph2.Node 不同。在 Java 中,上一个示例程序中的最后一行将是正确的。对于两个图的节点,Java 会分配相同的类型 Graph.Node;即 Node 前缀为类 Graph。在 Scala 中,这样的类型也可以表示,它写为 Graph#Node。如果我们想要连接不同图的节点,我们必须按以下方式更改初始图实现的定义

class Graph {
  class Node {
    var connectedNodes: List[Graph#Node] = Nil
    def connectTo(node: Graph#Node): Unit = {
      if (!connectedNodes.exists(node.equals)) {
        connectedNodes = node :: connectedNodes
      }
    }
  }
  var nodes: List[Node] = Nil
  def newNode: Node = {
    val res = new Node
    nodes = res :: nodes
    res
  }
}
class Graph:
  class Node:
    var connectedNodes: List[Graph#Node] = Nil
    def connectTo(node: Graph#Node): Unit =
      if !connectedNodes.exists(node.equals) then
        connectedNodes = node :: connectedNodes

  var nodes: List[Node] = Nil
  def newNode: Node =
    val res = Node()
    nodes = res :: nodes
    res

此页面的贡献者