S-JIS[2013-06-08] 変更履歴

Scala Dynamic

ScalaのType Dynamicのメモ。


DynamicはScala2.10で導入されたトレイト
このトレイトをミックスインして特定のメソッドを記述すると、フィールド操作のようなコーディングで値の更新・取得が出来る。
updateapplyメソッドを実装すると特殊な記法が出来るのと同様)
(Dynamicトレイト自体には何も実装されていない)

Dynamicを使うにはdynamicsパッケージをインポートしておく必要がある。

import scala.language.dynamics
備考
class MyMap extends Dynamic {
  val map = Map("abc"->123, "def"->456, "ghi"->789)

  def selectDynamic(key: String): Int = {
    map.getOrElse(key, 0)
  }
}
scala> val m = new MyMap
m: MyMap = MyMap@13882c0

scala> m.selectDynamic("abc")
res12: Int = 123

scala> m.abc
res13: Int = 123

scala> m.zzz
res14: Int = 0
scala> m.def
<console>:1: error: identifier expected but 'def' found.

scala> m.`def`
res15: Int = 456
Dynamicトレイトを継承して
selectDynamicというメソッドを実装すると、
フィールド取得の形式(obj.field)で
selectDynamicメソッドを呼べる。

なお、フィールド名を指定する部分では
Scalaのキーワード(def等)は使えない。
バッククォートで囲めば大丈夫。
こういう時の為のバッククォート(笑)

class MyProperties extends Dynamic {
  val map = scala.collection.mutable.Map.empty[String, Any]

  def updateDynamic(key: String)(value: Any): Unit = {
    map(key) = value
  }

  def selectDynamic(key: String): Any = {
    map.getOrElse(key, "")
  }
}
scala> val p = new MyProperties
p: MyProperties = MyProperties@3d2429

scala> p.abc = 111
p.abc: Any = 111

scala> p.updateDynamic("def")(222)

scala> p.abc
res22: Any = 111

scala> p.selectDynamic("abc")
res23: Any = 111

scala> p.zzz
res24: Any = ""
updateDynamic(とselectDynamic)メソッドを実装すると、
フィールド設定・取得の形式でメソッドが呼べる。
 
class MyArray extends Dynamic {
  val a = Array("a1", "a2", "a3")
  val b = Array("b", "bb", "bbb")

  def applyDynamic(key: String)(index: Int): String = {
    key match {
      case "a" => a(index)
      case "b" => b(index)
      case _   => ""
    }
  }
}
scala> val arr = new MyArray
arr: MyArray = MyArray@19e2223

scala> arr.a(1)
res29: String = a2

scala> arr.b(1)
res30: String = bb

scala> arr.z(0)
res31: String = ""
applyDynamicメソッドを実装すると、
配列の形式(obj.field(index))でメソッドが呼べる。
class MyArray extends Dynamic {
  val a = Array("a1", "a2", "a3")
  val b = Array("b", "bb", "bbb")

  def selectDynamic(key: String) = new {
    def update(index: Int, value: String): Unit = {
      key match {
        case "a" => a(index) = value
        case "b" => b(index) = value
        case _   =>
      }
    }
  }

  def applyDynamic(key: String)(index: Int): String = {
    key match {
      case "a" => a(index)
      case "b" => b(index)
      case _   => ""
    }
  }
}
scala> val arr = new MyArray
arr: MyArray = MyArray@650952

scala> arr.a(1)
res35: String = a2

scala> arr.a(1) = "zzz"

scala> arr.a(1)
res37: String = zzz
selectDynamicメソッドを実装して
updateメソッドを持つクラスを返すと
配列への代入の形式も扱うことが出来る。
class MyName extends Dynamic {
  val map = scala.collection.mutable.Map.empty[String, Map[String, Int]]

  def applyDynamicNamed(key: String)(vs: (String, Int)*): Unit = {
    map(key) = vs.toMap
  }

  def selectDynamic(key: String): Map[String, Int] = {
    map.getOrElse(key, Map.empty)
  }

  def applyDynamic(key: String)(s: String): Int = {
    selectDynamic(key).getOrElse(s, -1)
  }
}
scala> val n = new MyName
n: MyName = MyName@1ffbfdb

scala> n.zzz(aaa = 123, bbb = 456, 789)	//applyDynamicNamed

scala> n.zzz	//selectDynamic
res39: Map[String,Int] = Map(aaa -> 123, bbb -> 456, "" -> 789)

scala> n.zzz("aaa")	//applyDynamic
res40: Int = 123

scala> n.zzz("xyz")	//applyDynamic
res41: Int = -1
名前付き引数で代入できるようにする例。

Scala目次へ戻る / 技術メモへ戻る
メールの送信先:ひしだま