Understand how to use apply and unapply

二次信任 提交于 2021-01-20 14:24:21

问题


I'm trying to get a better understanding of the correct usage of apply and unapply methods.

Considering an object that we want to serialize and deserialize, is this a correct usage (i.e. the Scala way) of using apply and unapply?

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

回答1:


Firstly, apply and unapply are not necessarily opposites of each other. Indeed, if you define one on a class/object, you don't have to define the other.

apply

apply is probably the easier to explain. Essentially, when you treat your object like a function, apply is the method that is called, so, Scala turns:

obj(a, b, c) to obj.apply(a, b, c).

unapply

unapply is a bit more complicated. It is used in Scala's pattern matching mechanism and its most common use I've seen is in Extractor Objects.

For example, here's a toy extractor object:

object Foo {
  def unapply(x : Int) : Option[String] = 
    if(x == 0) Some("Hello, World") else None
}

So now, if you use this is in a pattern match like so:

myInt match {
    case Foo(str) => println(str)
}

Let's suppose myInt = 0. Then what happens? In this case Foo.unapply(0) gets called, and as you can see, will return Some("Hello, World"). The contents of the Option will get assigned to str so in the end, the above pattern match will print out "Hello, world".

But what if myInt = 1? Then Foo.unapply(1) returns None so the corresponding expression for that pattern does not get called.

In the case of assignments, like val Foo(str) = x this is syntactic sugar for:

val str : String = Foo.unapply(x) match {
  case Some(s) => s
  case None    => throw new scala.MatchError(x)
}



回答2:


So apply and unapply are just defs that have extra syntax support.

Apply takes arguments and by convention will return a value related to the object's name. If we take Scala's case classes as "correct" usage then the object Foo's apply will construct a Foo instance without needing to add "new". You are free of course to make apply do whatever you wish (key to value in Map, set contains value in Set, and indexing in Seq come to mind).

Unapply, if returning an Option or Boolean can be used in match{} and pattern matching. Like apply it's just a def so can do whatever you dream up but the common usage is to extract value(s) from instances of the object's companion class.

From the libraries I've worked with serialization/deserialization defs tend to get named explicitly. E.g., write/read, show/read, toX/fromX, etc.

If you want to use apply/unapply for this purpose the only thing I'd suggest is changing to

def unapply(f: Foo): Option[JValue]

Then you could do something like:

val myFoo = Foo("""{name: "Whiskers", age: 7}""".asJson)
// use myFoo

val Foo(jval) = myFoo
// use jval



回答3:


The apply method is like a constructor which takes arguments and creates an object, whereas the unapply takes an object and tries to give back the arguments.

A simple example:

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
    }
  }

when you call

val file = Foo("test", "txt")

It actually calls Foo.apply("test", "txt") and returns test.txt

If you want to deconstruct, call

val Foo(name) = file

This essentially invokes val name = Foo.unapply(file).get and returns (test, txt) (normally use pattern matching instead)

BTW, the return type of unapply is Option by convention.



来源:https://stackoverflow.com/questions/18468786/understand-how-to-use-apply-and-unapply

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!