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 以求清晰,但編譯器也能推論出來。這是因為當我們呼叫 graph1.newNode,而它呼叫 new Node 時,此方法使用特定於 graph1 這個實例的 Node 實例。

如果我們現在有兩個圖形,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

此頁面的貢獻者