我试图更好地理解apply
和unapply
方法的正确用法。
考虑到我们想要序列化和反序列化的对象,这是使用 and的正确用法(即Scala 方式)吗?apply
unapply
case class Foo
object Foo {
apply(json: JValue): Foo = json.extract[Foo]
unapply(f: Foo): JValue = //process to json
}
我试图更好地理解apply
和unapply
方法的正确用法。
考虑到我们想要序列化和反序列化的对象,这是使用 and的正确用法(即Scala 方式)吗?apply
unapply
case class Foo
object Foo {
apply(json: JValue): Foo = json.extract[Foo]
unapply(f: Foo): JValue = //process to json
}
首先,apply
并不unapply
一定是相互对立的。实际上,如果您在类/对象上定义一个,则不必定义另一个。
apply
可能更容易解释。本质上,当您将对象视为函数时,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
so,上面的模式匹配会打印出“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)
}
该apply
方法就像一个构造函数,它接受参数并创建一个对象,而unapply
接受一个对象并尝试返回参数。
一个简单的例子:
object Foo {
def apply(name: String, suffix: String) = name + "." + suffix
def unapply(name: String): Option[(String, String)] = {
//simple argument extractor
val parts = name.split("\\.")
if (parts.length == 2) Some(parts(0), parts(1)) else None
}
}
你打电话时
val file = Foo("test", "txt")
它实际上调用Foo.apply("test", "txt")
并返回test.txt
如果要解构,请致电
val Foo(name) = file
这实质上调用val name = Foo.unapply(file).get
并返回(test, txt)
(通常使用模式匹配代替)
也可以直接解包带有 2 个变量的元组,即
scala> val Foo(name, suffix) = file
val name: String = test
val suffix: String = txt
顺便说一句,返回类型unapply
是Option
约定俗成的。
所以 apply 和 unapply 只是具有额外语法支持的定义。
Apply 接受参数,按照惯例,将返回与对象名称相关的值。如果我们将 Scala 的案例类视为“正确”用法,那么对象 Foo 的 apply 将构造一个 Foo 实例,而无需添加“new”。你当然可以自由地让 apply 做任何你想做的事情(Map 中值的键,set 包含 Set 中的值,以及 Seq 中的索引)。
如果返回 Option 或 Boolean 可用于 match{} 和模式匹配,则不适用。就像 apply 它只是一个 def 所以可以做任何你想做的事情,但常见的用法是从对象的伴生类的实例中提取值。
从我使用过的序列化/反序列化库中,defs 倾向于明确命名。例如,写入/读取、显示/读取、toX/fromX 等。
如果您想为此目的使用应用/取消应用,我唯一建议的就是更改为
def unapply(f: Foo): Option[JValue]
然后你可以做类似的事情:
val myFoo = Foo("""{name: "Whiskers", age: 7}""".asJson)
// use myFoo
val Foo(jval) = myFoo
// use jval