Szukaj…


Wprowadzenie

Scala pozwala korzystać z dynamicznego wywoływania podczas wywoływania metod lub uzyskiwania dostępu do pól na obiekcie. Zamiast wbudować to głęboko w język, osiąga się to poprzez przepisywanie reguł podobnych do reguł ukrytych konwersji, włączanych przez cechę markera [ scala.Dynamic ] [Dynamic scaladoc]. Umożliwia to emulowanie możliwości dynamicznego dodawania właściwości do obiektów obecnych w dynamicznych językach i wiele innych. [Dynamic scaladoc]: http://www.scala-lang.org/api/2.12.x/scala/Dynamic.html

Składnia

  • klasa Foo rozszerza Dynamic
  • foo.field
  • foo.field = wartość
  • foo.method (args)
  • foo.method (namedArg = x, y)

Uwagi

W celu zadeklarowania podtypy Dynamic funkcja język dynamics musi być włączony, albo poprzez importowanie scala.language.dynamics lub przez -language:dynamics kompilatora opcji. Użytkownicy tej Dynamic którzy nie definiują własnych podtypów, nie muszą tego włączać.

Dostęp do pola

To:

class Foo extends Dynamic {
  // Expressions are only rewritten to use Dynamic if they are not already valid
  // Therefore foo.realField will not use select/updateDynamic
  var realField: Int = 5
  // Called for expressions of the type foo.field
  def selectDynamic(fieldName: String) = ???
  def updateDynamic(fieldName: String)(value: Int) = ???
}

pozwala na łatwy dostęp do pól:

val foo: Foo = ???
foo.realField // Does NOT use Dynamic; accesses the actual field
foo.realField = 10 // Actual field access here too
foo.unrealField // Becomes foo.selectDynamic(unrealField)
foo.field = 10  // Becomes foo.updateDynamic("field")(10)
foo.field = "10" // Does not compile; "10" is not an Int.
foo.x() // Does not compile; Foo does not define applyDynamic, which is used for methods.
foo.x.apply() // DOES compile, as Nothing is a subtype of () => Any
// Remember, the compiler is still doing static type checks, it just has one more way to
// "recover" and rewrite otherwise invalid code now.

Wywołania metod

To:

class Villain(val minions: Map[String, Minion]) extends Dynamic {
  def applyDynamic(name: String)(jobs: Task*) = jobs.foreach(minions(name).do)
  def applyDynamicNamed(name: String)(jobs: (String, Task)*) = jobs.foreach {
    // If a parameter does not have a name, and is simply given, the name passed as ""
    case ("", task) => minions(name).do(task)
    case (subsys, task) => minions(name).subsystems(subsys).do(task)
  }
}

pozwala na wywoływanie metod z nazwanymi parametrami i bez nich:

val gru: Villain = ???
gru.blu() // Becomes gru.applyDynamic("blu")()
// Becomes gru.applyDynamicNamed("stu")(("fooer", ???), ("boomer", ???), ("", ???),
//         ("computer breaker", ???), ("fooer", ???))
// Note how the `???` without a name is given the name ""
// Note how both occurrences of `fooer` are passed to the method
gru.stu(fooer = ???, boomer = ???, ???, `computer breaker` = ???, fooer = ???)
gru.ERR("a") // Somehow, scalac thinks "a" is not a Task, though it clearly is (it isn't)

Interakcja między dostępem do pola a metodą aktualizacji

Nieco intuicyjnie (ale także jedyny rozsądny sposób, aby to działało):

val dyn: Dynamic = ???
dyn.x(y) = z

jest równa:

dyn.selectDynamic("x").update(y, z)

podczas

dyn.x(y)

jest wciąż

dyn.applyDynamic("x")(y)

Ważne jest, aby zdawać sobie z tego sprawę, w przeciwnym razie może się zakraść niezauważony i spowodować dziwne błędy.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow