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.

Język programowania Scala Wydanie 2. Copyright © Grzegorz Balcerek 2016

Licencja Creative Commons

Ten utwór jest dostępny na licencji Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0 Międzynarodowe.

Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners.