Понять, как использовать применение и неприменение
Я пытаюсь лучше понять правильное использование методов apply
и unapply
.
Рассматривая объект, который мы хотим сериализовать и десериализовать, является ли это правильное использование (то есть способ Scala) использования apply
и unapply
?
case class Foo
object Foo {
apply(json: JValue): Foo = json.exctract[Foo]
unapply(f: Foo): JValue = //process to json
}
Ответы
Ответ 1
Во-первых, apply
и unapply
не обязательно являются противоположностями друг друга. В самом деле, если вы определяете одно из класса/объекта, вам не нужно определять другое.
подать выражение
apply
, вероятно, легче объяснить. По сути, когда вы обрабатываете свой объект как функцию, применяете метод, который вызывается, поэтому Scala поворачивается:
obj(a, b, c)
для obj.apply(a, b, c)
.
Исключить
unapply
немного сложнее. Он используется в механизме сопоставления шаблонов Scala, и его наиболее распространенное использование я видел в Extractor Objects.
Например, здесь объект-экстрактор игрушек:
object Foo {
def unapply(x : Int) : Option[String] =
if(x == 0) Some("Hello, World") else None
}
Итак, теперь, если вы используете это в соответствии с шаблоном:
myInt match {
case Foo(str) => println(str)
}
Предположим, что myInt = 0
. Тогда что происходит? В этом случае Foo.unapply(0)
, и, как вы можете видеть, возвращает Some("Hello, World")
. Содержимое Option
будет назначено на str
поэтому в конце этого совпадения будет напечатан "Hello, world".
Но что, если myInt = 1
? Затем Foo.unapply(1)
возвращает None
поэтому соответствующее выражение для этого шаблона не вызывает.
В случае присвоений, таких как val Foo(str) = x
это синтаксический сахар для:
val str : String = Foo.unapply(x) match {
case Some(s) => s
case None => throw new scala.MatchError(x)
}
Ответ 2
Таким образом, применение и unapply - это просто defs, которые имеют дополнительную поддержку синтаксиса.
Применить принимает аргументы и по соглашению возвращает значение, относящееся к имени объекта. Если мы возьмем классы Case Scala как "правильное" использование, то объект Foo apply будет строить экземпляр Foo без необходимости добавления "нового". Вы, конечно же, свободны, чтобы сделать приложение делать все, что вы пожелаете (ключ к значению в Map, set содержит значение в Set и индексирование в Seq приходят на ум).
Unapply, если возврат Option или Boolean можно использовать в match {} и сопоставлении с образцом. Например, примените его только к def, поэтому можете делать все, что вам нужно, но общее использование - извлечение значений из экземпляров класса сопутствующих объектов.
Из библиотек, которые я работал с сериализацией/десериализацией, defs имеет тенденцию явно указывать имена. Например, писать/читать, показывать/читать, toX/fromX и т.д.
Если вы хотите использовать apply/unapply для этой цели, единственное, что я предлагаю, это изменить на
def unapply(f: Foo): Option[JValue]
Тогда вы можете сделать что-то вроде:
val myFoo = Foo("""{name: "Whiskers", age: 7}""".asJson)
// use myFoo
val Foo(jval) = myFoo
// use jval