您的位置:首页 > 移动开发

Desugar Scala(15) -- unapply和unapplySeq方法

2017-04-20 12:56 351 查看
欢迎关注我的新博客地址:http://cuipengfei.me/

实在想不到什么动词能够当做脱衣服来讲了。所以从如今開始这系列博文就叫做Desugar Scala了。除非哪天才思泉涌,又想到了新词:)

開始正文。

名字叫做unapply和unapplySeq的方法在Scala里也是有特殊含义的。

我们前面说过case class在做pattern match时非常好用,而除case class之外,有unapply或unapplySeq方法的对象在pattern match时也有非常好的应用场景。

比方这段代码:

1
2
3

object Square {
def unapply(z: Double): Option[Double] = Some(math.sqrt(z))
}

我们定义了一个unapply方法,用来计算平方根。

我们能够像调用普通方法一样的调用它:

1
2

val number: Double = 36.0
Square.unapply(number)

这样会得到36的平方根:6。实际上返回值是Some(6)。

上面的方式是对unapply的浪费。unapply真正的优点是这种:

1
2
34
5

val number: Double = 36.0
number match {
case Square(n) => println(s"square root of $number is $n")
case _ => println("nothing matched")
}

这样我们无需显式调用unapply方法,而把是它用在pattern match中。让编译器替我们调用它。

当我们写下这段pattern match的代码时,编译器事实上替我们做了好几件事:

调用unapply,传入number

接收返回值并推断返回值是None,还是Some

假设是Some,则将其解开,并将当中的值赋值给n(就是case Square(n)中的n)

这段代码反编译出来是这个样子的:

1
2
34
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

double number = 36.0D;
double d1 = number;
Option localOption = Square..MODULE$.unapply(d1);
//调用unapply,传入number
BoxedUnit localBoxedUnit;
if (localOption.isEmpty()) {//推断返回值是None
Predef..MODULE$.println("nothing matched");
localBoxedUnit = BoxedUnit.UNIT;
}
else {//推断返回值是Some
double n = BoxesRunTime.unboxToDouble(localOption.get());
//将Some解开,并将当中的值赋值给n
Predef..MODULE$.println(new StringContext(Predef..MODULE$.wrapRefArray((Object[]) new String[] {
"square root of ", " is ", ""
}) ).s(Predef..MODULE$.genericWrapArray(new Object[] {
BoxesRunTime.boxToDouble(number), BoxesRunTime.boxToDouble(n)
})));
localBoxedUnit = BoxedUnit.UNIT;
}

假设没有unapply方法和pattern match语法之间的这样的结合,我们自己写代码要写成什么样子呢?

也许会比上面反编译的代码简单一些,可是显式地调用开平方的方法。用if else来推断Option,以及将真正的返回值从Option里面解出来这三件事是免不掉的。

unapplySeq和unapply的作用非常是类似,比如这样:

1
2
34
5
6

object Names {
def unapplySeq(str: String): Option[Seq[String]] = {
if (str.contains(",")) Some(str.split(","))
else None
}
}

我们定义一个unapplySeq方法,用逗号作为分隔符来把字符串拆开。

然后我们能够这样应用它:

1
2
34
5
6
7
8

val namesString = "xiao ming,xiao hong,tom"
namesString match {
case Names(first, second, third) => {
println("the string contains three people's names")
println(s"$first $second $third")
}
case _ => println("nothing matched")
}

与上面的样例非常是类似,只是编译器在这里替我们做的事情很多其它了:

调用unapplySeq,传入namesString

接收返回值并推断返回值是None,还是Some

假设是Some,则将其解开

推断解开之后得到的sequence中的元素的个数是否是三个

假设是三个,则把三个元素分别取出,赋值给first,second和third

假设没有unapplySeq方法和pattern match语法之间的这样的结合,我们自己写代码来做这五件事会显得非常是繁琐。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: