Scala的apply()、unapply()、unapplySeq()方法

apply通常被称作注入方法

unapply通常被称为提取方法,使用unapply来提取固定数量的对象,使用unapplySeq来提取一个序列


1、apply用法:对象(),默认调用该对象的apply方法

  
  
  1. object ApplyAndUnapply
  2. {
  3. def main(args: Array[String]): Unit =
  4. {
  5. ApplyAndUnapply()
  6. }
  7. def apply()
  8. {
  9. println("this is apply()")
  10. }
  11. }

2、名字叫做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
3
4
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的代码时,编译器其实替我们做了好几件事:

  1. 调用unapply,传入number
  2. 接收返回值并判断返回值是None,还是Some
  3. 如果是Some,则将其解开,并将其中的值赋值给n(就是case Square(n)中的n)

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

1
2
3
4
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
3
4
5
6 
object Names {
 def unapplySeq(str: String): Option[Seq[String]] = {  if (str.contains(",")) Some(str.split(","))  else None  } } 

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

然后我们可以这样应用它:

1
2
3
4
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") } 

与上面的例子很是类似,不过编译器在这里替我们做的事情更多了:

  1. 调用unapplySeq,传入namesString
  2. 接收返回值并判断返回值是None,还是Some
  3. 如果是Some,则将其解开
  4. 判断解开之后得到的sequence中的元素的个数是否是三个
  5. 如果是三个,则把三个元素分别取出,赋值给first,second和third

如果没有unapplySeq方法和pattern match语法之间的这种结合,我们自己写代码来做这五件事会显得很是繁琐。











评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值