Tour of Scala

内部クラス

Language

Scalaではクラスが他のクラスをメンバーとして保持することが可能です。 Javaのような、内部クラスが外側のクラスのメンバーとなる言語とは対照的に、Scalaでは、内部クラスは外側のオブジェクトに束縛されます。 どのノードがどのグラフに属しているのかを私達が混同しないように、コンパイラがコンパイル時に防いでほしいのです。 パス依存型はその解決策の1つです。

その違いを示すために、グラフデータ型の実装をさっと書きます。

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
  }
}

このプログラムはグラフをノードのリスト(List[Node])で表現しています。いずれのノードも接続している他のノードへのリスト(connectedNodes)を保持します。class Nodeclass 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 Nodeを呼んでいるgraph1.newNodeを呼び出す時、メソッドがNodeのインスタンスgraph1を使用しているからです。

2つのグラフがあるとき、Scalaの型システムは1つのグラフの中で定義されたノードと別のグラフで定義されたノードを混ぜることを許しません。 それは別のグラフのノードは別の型を持つからです。 こちらは不正なプログラムです。

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.Nodegraph2.Nodeとは異なります。Javaであれば先のプログラム例の最後の行は正しいでしょう。 2つのグラフのノードに対して、Javaは同じ型Graph.Nodeを指定します。つまりクラスGraphNodeの接頭辞です。 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
  }
}

Contributors to this page: