强大的Scala模式匹配
用过Scala的模式匹配,感觉Java的弱爆了。Scala几乎可以匹配任何数据类型,如果默认的不能满足你的要求,你可以自定义模式匹配。
介绍Scala的模式匹配前,我们先了解清楚unapply()与unapplySeq()两个方法:
名字叫做unapply和unapplySeq的方法在Scala里也是有特殊含义的。
我们前面说过case class在做pattern match时非常好用,而除case class之外,有unapply或unapplySeq方法的对象在pattern match时也有非常好的应用场景。
比方这段代码:
1 |
|
我们定义了一个unapply方法,用来计算平方根。
我们能够像调用普通方法一样的调用它:
1 |
|
这样会得到36的平方根:6。实际上返回值是Some(6)。
上面的方式是对unapply的浪费。unapply真正的优点是这种:
1 |
|
这样我们无需显式调用unapply方法,而把是它用在pattern match中。让编译器替我们调用它。
当我们写下这段pattern match的代码时,编译器事实上替我们做了好几件事:
- 调用unapply,传入number
- 接收返回值并推断返回值是None,还是Some
- 假设是Some,则将其解开,并将当中的值赋值给n(就是case Square(n)中的n)
这段代码反编译出来是这个样子的:
1 |
|
假设没有unapply方法和pattern match语法之间的这样的结合,我们自己写代码要写成什么样子呢?
也许会比上面反编译的代码简单一些,可是显式地调用开平方的方法。用if else来推断Option,以及将真正的返回值从Option里面解出来这三件事是免不掉的。
unapplySeq和unapply的作用非常是类似,比如这样:
1 |
|
我们定义一个unapplySeq方法,用逗号作为分隔符来把字符串拆开。
然后我们能够这样应用它:
1 |
|
与上面的样例非常是类似,只是编译器在这里替我们做的事情很多其它了:
- 调用unapplySeq,传入namesString
- 接收返回值并推断返回值是None,还是Some
- 假设是Some,则将其解开
- 推断解开之后得到的sequence中的元素的个数是否是三个
- 假设是三个,则把三个元素分别取出,赋值给first,second和third
假设没有unapplySeq方法和pattern match语法之间的这样的结合,我们自己写代码来做这五件事会显得非常是繁琐。
大家了解清楚unapply()与unapplySeq()两个方法,我就举个很实用的例子。
现实中往往我们有这样的一个需求:比如http请求返回体为json字符串,我们往往只需要获取json中的部分字段值。
这个需求,Java的做法是,必须知道所有json中所有字段及类型,并定义对应的JavaBean,将返回的json字符串先转换为对应的JavaBean,再通过javaBean获取想要的字段信息。
那么Scala使用模式匹配就很轻松搞定直接获取想要的字段值。如下:
先定义一个unapplySeq()实现json的模式匹配(另json字符串插值写法可参考我以前文章:Scala字符串插值 - StringContext):
package spray.json
import scala.collection.SortedMap
class JsonInterpolation(sc: StringContext) {
object json {
def apply(args: JsValue*): JsValue =
new JsonParser(ParserInput(sc, args), true).parseJsValue() def unapplySeq(input: JsValue): Option[Seq[JsValue]] = { val placeHolders = Seq.range(0, sc.parts.length-1).map(x => JsNumber(Integer.MAX_VALUE - x) ) val pi = ParserInput(sc, placeHolders)
val pattern = new JsonParser(pi, true).parseJsValue() val results = collection.mutable.ArrayBuffer[JsValue]()
Seq.range(0, sc.parts.length-1).foreach { x => results += null } try {
patternMatch(pattern, input, placeHolders, results)
Some(results.toSeq)
}
catch {
case ex: Throwable => None
} } // TODO report friendly
private def patternMatch(pattern: JsValue, input: JsValue, placeHolders: Seq[JsValue], results: collection.mutable.ArrayBuffer[JsValue]): Unit = { def isPlaceHolder(value: JsNumber) = {
val num = value.value.toInt
val index = Integer.MAX_VALUE - num.toInt
num > 0 && index < placeHolders.size && placeHolders(index).eq(value)
} pattern match {
case x: JsObject =>
x.fields.foreach {
case (key, n @ JsNumber(num)) if isPlaceHolder(n) =>
val index = Integer.MAX_VALUE - num.toInt
assert(input.asJsObject.fields contains key)
results(index) = input.asJsObject.fields(key) case (key, value) =>
assert(input.asJsObject.fields contains key)
patternMatch(value, input.asJsObject.fields(key), placeHolders, results)
}
case x: JsArray =>
assert(input.isInstanceOf[JsArray])
assert(input.asInstanceOf[JsArray].elements.size >= x.elements.size)
x.elements.zipWithIndex.foreach {
case (x: JsNumber, y: Int) if isPlaceHolder(x) =>
val index = Integer.MAX_VALUE - x.value.toInt
results(index) = input.asInstanceOf[JsArray].elements(y)
case (x: JsValue,y: Int)=>
patternMatch(x, input.asInstanceOf[JsArray].elements.apply(y), placeHolders, results)
}
case x: JsString =>
assert(x == input)
case x: JsBoolean =>
assert(x == input)
case x: JsNumber =>
assert(x == input)
case x@ JsNull =>
assert(x == input)
}
}
}
}
定义好unpplySeq(),那么下面就看下如何通过模式匹配获取json字符传中我们关系的字段值:
import spray.json._ /**
* 类功能描述:
*
* @author WangXueXing create at 19-5-11 下午10:37
* @version 1.0.0
*/
object ObjectMatch {
def main(args: Array[String]): Unit = {
val json = json"""{"id": 12333,"sku_no":"sku35352523"}"""
json match {
case json"""{"sku_no": $skuNo}""" => println(skuNo)
case _ => println(json)
} }
}
输出:
"sku35352523"
如上,我们就可以选择性的拿到sku_no字段的值,是不是轻松搞定!
强大的Scala模式匹配的更多相关文章
- scala模式匹配详细解析
一.scala模式匹配(pattern matching) pattern matching可以说是scala中十分强大的一个语言特性,当然这不是scala独有的,但这不妨碍它成为scala的语言的一 ...
- scala模式匹配的使用
Scala模式匹配 Tip1:模式总是从上往下匹配,如果匹配不到则匹配case_项(类似Java中的default) Tip2:与Java和C语言不同,不需要在每个分支末尾使用break语句退出(不会 ...
- Spark记录-Scala模式匹配
Scala模式匹配 模式匹配是Scala函数值和闭包后第二大应用功能.Scala为模式匹配提供了极大的支持,处理消息. 模式匹配包括一系列备选项,每个替代项以关键字大小写为单位.每个替代方案包括一个模 ...
- Scala模式匹配和类型系统
1.模式匹配比java中的switch case强大很多,除了值,类型,集合等进行匹配,最常见的Case class进行匹配,Master.scala有大量的模式匹配. Case "_&qu ...
- scala模式匹配与样例类
样本类:添加了case的类便是样本类.这种修饰符可以让Scala编译器自动为这个类添加一些语法上的便捷设定.如下: 1.添加与类名一致的工厂方法.也就是说,可以写成Var("x") ...
- Scala模式匹配| 隐式转换
1. 模式匹配 Scala中的模式匹配类似于Java中的switch语法,但是更加强大.模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需要匹配时,会从第一个case分 ...
- Scala模式匹配和样例类
Scala有一个十分强大的模式匹配机制,可以应用到很多场合:如switch语句.类型检查等.并且Scala还提供了样例类,对模式匹配进行了优化,可以快速进行匹配. 1.字符匹配 def mai ...
- 10、scala模式匹配
一.模式匹配1 1.介绍 模式匹配是Scala中非常有特色,非常强大的一种功能.模式匹配,其实类似于Java中的swich case语法,即对一个值进行条件判断,然后针对不同的条件, 进行不同的处理. ...
- 12. Scala模式匹配
12.1 match 12.1.1 基本介绍 Scala中的模式匹配类似于Java中的switch语法,但是更加强大 模式匹配语法中,采用match关键字声明,每个分支采用case关键字进行声明,当需 ...
随机推荐
- Spring Boot自定义配置实现IDE自动提示
一.背景 官方提供的spring boot starter的配置项,我们用IDE配置的时候一般都有自动提示的,如下图所示 而我们自己自定义的配置却没有,对开发非常不友好容易打错配置,那这个是怎样实现的 ...
- Kafka学习笔记之Kafka High Availability(上)
0x00 摘要 Kafka在0.8以前的版本中,并不提供High Availablity机制,一旦一个或多个Broker宕机,则宕机期间其上所有Partition都无法继续提供服务.若该Broker永 ...
- GDAL读取Shapefile
-------------------------------------------------------------------------------------- #include < ...
- 关联mysql失败_Server returns invalid timezone. Go to 'Advanced' tab and set 'serverTimezon' 时区错误
时区错误,MySQL默认的时区是UTC时区,比北京时间晚8个小时. 所以要修改mysql的时长 在mysql的命令模式下,输入: set global time_zone='+8:00'; 再次连接成 ...
- 小鸟初学Shell编程(二)编写简单的Shell脚本
Shell脚本 编写Python.PHP脚本通常需要掌握语言的函数,那么Shell脚本则不需要,只需要掌握Linux命令就可以编写Shell脚本,因为Shell脚本就是由多个Linux命令组成,通过将 ...
- ubuntu 18.04 修改Apache默认目录
ubuntu 18.04 修改Apache默认目录 安装是直接运行 sudu apt install apache2 安装之后要修改目录 vi /etc/apache2/sites-available ...
- tornado中的options常用姿势
tornado是facebook开源的非阻塞web容器,类似java的netty,tornado.options是负责解析tornado容器的全局参数的,同时也能够解析命令行传递的参数和从配置文件中解 ...
- linux 互斥锁和条件变量
为什么有条件变量? 请参看一个线程等待某种事件发生 注意:本文是linux c版本的条件变量和互斥锁(mutex),不是C++的. mutex : mutual exclusion(相互排斥) 1,互 ...
- NACOS升级操作
Server端 0.8.0及以上版本: 解压安装包后替换{nacos.home}/target/nacos-server.jar 删除{nacos.home}/plugins/cmdb/及{nacos ...
- 从写下第1个脚本到年薪30W,我的自动化测试心路历程
我希望我的故事能够激励现在的软件测试人,尤其是还坚持在做“点点点”的测试人. 你可能会有疑问:“我也能做到这一点的可能性有多大?”因此,我会尽量把自己做决定和思考的过程讲得更具体一些,并尽量体现更多细 ...