7.17. Cecha Dynamic
Scala udostępnia specjalną cechę znacznikową o nazwie Dynamic, która nie definiuje żadnych składowych, ale jest w specjalny sposób traktowana przed kompilator. Odwołania do składowych typów implementujących tę cechę mogą być przekształcane na odwołania do metod o nazwach applyDynamic, applyDynamicNamed, updateDynamic albo selectDynamic. Plik Dynamic1.scala przedstawia przykład klasy Dynamic1, rozszerzającej cechę Dynamic i definiującej takie metody.
Plik Dynamic1.scala: import language.dynamics class Dynamic1 extends Dynamic { def selectDynamic(name: String) = println(s"selectDynamic($name)") def applyDynamic(name: String)(n: Int) = println(s"applyDynamic($name)($n)") def applyDynamicNamed(name: String)(arg1:(String,Int), arg2: (String,Long)) = println(s"applyDynamicNamed($name)($arg1, $arg2)") def updateDynamic(name: String)(a: Int): Unit = println(s"updateDynamic($name)($a)") }
Klauzula importu z wiersza zapobiega wystąpieniu błędu kompilatora. Rozszerzanie cechy Dynamic musi być umożliwione za pomocą flagi funkcjonalności dynamics. Flagi funkcjonalności są opisane w punkcie 13.5). |
Jeśli obiekt y implementuje cechę Dynamic, to wyrażenie y.x może być przetłumaczone na wywołanie metody selectDynamic z argumentem "x". Ilustruje to następujący przykład.
scala> val y = new Dynamic1 y: Dynamic1 = Dynamic1@114a7d4 scala> y.abc selectDynamic(abc)
Wyrażenie y.x(a,b,c) może być przetłumaczone na postać y.applyDynamic("x")(a,b,c) (analogicznie dla wyrażeń o innej liczbie argumentów).
scala> y.abc(1) applyDynamic(abc)(1)
Wyrażenie zawierające argument nazwany, na przykład y.x(a = 1, b = 2) może być przetłumaczone na wywołanie metody applyDynamicNamed z parametrami w postaci krotek na drugiej liście parametrów, w tym przypadku: applyDynamicNamed("x")(("a",1),("b",2)).
scala> y.abc(x=1, y=3L) applyDynamicNamed(abc)((x,1), (y,3)) scala> y.abc(x=1, 4L) applyDynamicNamed(abc)((x,1), (,4))
Wyrażenie y.x = a może być przetłumaczone na wywołanie y.applyUpdate("x")(a). Ilustruje to kolejny przykład.
scala> { y.abc = 5; () } updateDynamic(abc)(5)
W tym przykładzie wyrażenie jest umieszczone w bloku kodu zakończonym wartością () po to, aby uniknąć ewaluacji wyrażenia y.abc przez konsolę języka. |
Metody applyDynamic, applyDynamicNamed, updateDynamic i selectDynamic mogą mieć także parametry typu. Przykłady takich metod zawiera klasa Dynamic2 z pliku Dynamic2.scala.
Plik Dynamic2.scala: import language.dynamics class Dynamic2 extends Dynamic { def applyDynamic[T,S](name: String)(n: T, m: S) = println(s"applyDynamic($name)($n,$m)") def applyDynamicNamed[T](name: String)(arg1:(String,T)) = println(s"applyDynamicNamed($name)($arg1)") }
Poniżej pokazano przykładowe wywołania metod tej klasy.
scala> val z = new Dynamic2 z: Dynamic2 = Dynamic2@f5f647 scala> z.nonExistentMethod(1234, 'a) applyDynamic(nonExistentMethod)(1234,'a) scala> z.nonExistentMethod2[Int,Symbol](1234, 'a) applyDynamic(nonExistentMethod2)(1234,'a) scala> z.nonExistentMethod3(arg = 'ABC) applyDynamicNamed(nonExistentMethod3)((arg,'ABC)) scala> z.nonExistentMethod3[Symbol](arg = 'ABC) applyDynamicNamed(nonExistentMethod3)((arg,'ABC))
Z kolei poniższe próby wywołań metod są nieprawidłowe.
scala> z.nonExistentMethod(1234) <console>:12: error: not enough arguments for method applyDynamic: (n: T, m: S)Unit. Unspecified value parameter m. z.nonExistentMethod(1234) ^ scala> z.nonExistentMethod3[Long](arg = 'ABC) <console>:12: error: type mismatch; found : Symbol required: Long error after rewriting to z.applyDynamicNamed[Long]("nonExistentMethod3")(scala.Tuple2("arg", scala.Symbol("ABC"))) possible cause: maybe a wrong Dynamic method signature? z.nonExistentMethod3[Long](arg = 'ABC) ^
W wyrażeniu z wiersza podanych jest za mało argumentów. W wyrażeniu z wiersza nie zgadza się jawnie podany typ Long z typem wartości przekazanej jako argument.