了解如何使用apply和unapply

时间:2013-08-27 14:50:39

标签: scala

我正在努力更好地了解applyunapply方法的正确用法。

考虑我们想要序列化和反序列化的对象,这是使用applyunapply的正确用法(即 Scala方式)吗?

case class Foo
object Foo {
    apply(json: JValue): Foo = json.extract[Foo]
    unapply(f: Foo): JValue = //process to json
}

3 个答案:

答案 0 :(得分:52)

首先,applyunapply不一定是对方的对立面。实际上,如果在类/对象上定义一个,则不必定义另一个。

应用

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,因此最后,上述模式匹配将打印出“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)
}

答案 1 :(得分:4)

因此,apply和unapply只是具有额外语法支持的def。

Apply接受参数,按照惯例,将返回与对象名称相关的值。如果我们将Scala的case类作为“正确”用法,那么对象Foo的apply将构造一个Foo实例而不需要添加“new”。您可以自由地使应用做任何你想做的事情(在Map中设置值的关键,在Set中设置包含值,并在Seq中记录索引)。

取消应用,如果返回选项或布尔值,则可以在匹配{}和模式匹配中使用。就像应用它只是一个def所以可以做任何你梦想的事情,但常见的用法是从对象的伴侣类的实例中提取值。

从我使用序列化/反序列化的库中,defs倾向于明确命名。例如,写/读,显示/读,到X / 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

答案 2 :(得分:2)

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)(通常使用模式匹配)

顺便说一句,unapply的返回类型按照惯例是Option