Scala的模式匹配、偏函数、正则表达式、异常处理和提取器
1. 模式匹配
Scala中有一个强大的模式匹配机制,应用非常广泛,比如:
- 判断固定值
- 类型查询
- 快速获取数据
1.1 简单模式匹配
一个模式匹配包含了一些了备选项,每个备选项都开始于关键字case,且每个备选项都包含了一个模式及一到多个表达式。箭头符号=>隔开了模式和表达式。
格式:
变量 match {
case "常量1" => 表达式1
case "常量2" => 表达式2
case "常量3" => 表达式3
case _ => 表达式4 //默认匹配项
}
执行流程:

示例:

import scala.io.StdIn
object ClassDemo {
def main(args:Array[String]):Unit = {
//1.提示用户录入一个单词
println("请录入一个字符串:")
val s = StdIn.readLine()
//2.判断该单词是否能够匹配单词
val result = s match {
case "hadoop" => "大数据分布式存储和计算框架"
case "zookeeper" => "大数据分布式协调服务框架"
case "spark" => "大数据分布式内存计算框架"
case _ => "未匹配"
}
//3.打印结构
println(result)
//4.简写形式
s match {
case "hadoop" => println("大数据分布式存储和计算框架")
case "zookeeper" => println("大数据分布式协调服务框架")
case "spark" => println("大数据分布式内存计算框架")
case _ => println("未匹配")
}
}
}
1.2 匹配类型
除了匹配数据,match表达式还可以进行类型匹配。如我们要根据不同的数据类型,来执行不同的逻辑。也可以使用match表达式来实现。
格式:
变量 match {
case 变量名1:类型1 => 表达式1
case 变量名2:类型2 => 表达式2
case 变量名3:类型3 => 表达式3
case _ => 表达式4 //默认匹配项
}
注意:
当进行case校验时,变量没有在表达式中被使用,则通过下划线来代替变量名
示例:

object ClassDemo {
def main(args:Array[String]):Unit = {
//1.定义一个变量为Any类型
val a:Any = "hadoop"
//2.定义模式匹配
val result1 = a match {
case x:String => s"${x} 是String类型"
case x:Int => s"${x} 是Int类型"
case x:Double => s"${x} 是Double类型"
case _ => "未匹配"
}
//3.打印结构
println(result1) //hadoop 是String类型
//4.简写形式:当进行case校验时,变量没有在表达式中被使用,则通过下划线来代替变量名
val result2 = a match {
case _:String => "String"
case _:Int => "Int"
case _:Double => "Double"
case _ => "未匹配"
}
//5.打印结构
println(result2) //Double
}
}
1.3 守卫
守卫指的是在case语句中添加if条件判断,使得我们代码更简洁
格式:
变量 match {
case 变量名 if条件1 => 表达式1
case 变量名 if条件2 => 表达式2
case 变量名 if条件3 => 表达式3
case _ => 表达式4
}
示例:

import scala.io.StdIn
object ClassDemo {
def main(args:Array[String]):Unit = {
//1.从控制台读入一个数字
println("录入一个整数:")
val a = StdIn.readInt()
//2.定义模式匹配
a match {
case x if x >= 0 && x <= 3 => println("[0-3]")
case x if x >= 4 && x <=8 => println("[4-8]")
case _ => println("未匹配")
}
}
}
1.4 匹配样例类
Scala中可以使用模式匹配来匹配样例类,从而实现快速获取样例类中的成员数据。在开发Akka中会涉及。
格式:
对象名 match {
case 样例类型1(字段1, 字段2, 字段n) => 表达式1
case 样例类型2(字段1, 字段2, 字段n) => 表达式2
case 样例类型3(字段1, 字段2, 字段n) => 表达式3
case _ => 表达式4
}
注意:
- 样例类型后的小括号中,编写的字段个数要和该样例类的字段个数保持一致
- 通过match进行模式匹配时,要匹配的对象必须声明为:Any类型
示例:

object ClassDemo {
//1.创建两个样例类Customer和Order
case class Customer(name:String, age:Int)
case class Order(id:Int)
def main(args:Array[String]):Unit = {
//2.分别定义两个样例类的对象,并指定为Any类型
val c:Any = Customer("lee", 23)
//3.模式匹配这两个对象
c match {
case Customer(a, b) => println("Customer类对象")
case Order(a) => println("Order类对象")
case _ => println("未匹配")
}
}
}
//Customer类对象
1.5 匹配集合
Scala的模式匹配还能用来匹配数组、元组、集合(列表、集、映射)等。
示例一:匹配数组

object ClassDemo {
def main(args:Array[String]):Unit = {
//1.定义三个数组
val arr1 = Array(1, 2, 3)
val arr2 = Array(0)
val arr3 = Array(0, 1, 2, 3, 4, 5)
//2.模式匹配
arr1 match {
case Array(1, x, y) => println("第一类数组")
case Array(0) => println("第二类数组")
case Array(0, _*) => println("第三类数组") //下划线表示任意多个元素
case _ => println("未匹配")
}
}
}
//第一类数组
示例二:匹配列表

object ClassDemo {
def main(args:Array[String]):Unit = {
//1.定义三个数组
val list1 = List(0)
val list2 = List(0, 1, 2, 3)
val list3 = List(11, 22)
//2.模式匹配
//思路一:通过List()实现
list1 match {
case List(0) => println("第一类列表")
case List(0, _*) => println("第二类列表")
case List(x, y) => println("第三类列表")
}
//思路二:通过关键字实现,Nil,tail
list1 match {
case 0::Nil => println("第一类列表")
case 0::tail => println("第二类列表")
case x::y::Nil => println("第三类列表")
}
}
}
//第一类列表
示例三: 匹配元组

object ClassDemo {
def main(args:Array[String]):Unit = {
//1.定义元组
val tuple1 = (1, 2, 3)
val tuple2 = (3, 4, 5)
//2.模式匹配
//思路一:通过List()实现
tuple1 match {
case (1, x, y) => println("第一类元组")
case (x, y, 5) => println("第二类元组")
case _ => println("未匹配")
}
}
}
//第一类元组
1.6 变量声明中的模式匹配
在定义变量时,可以使用模式匹配快速获取数据,例如:快速从数组,列表中获取数据
示例:

object ClassDemo {
def main(args:Array[String]):Unit = {
//1.
var arr = (0 to 10).toArray
val Array(_, x, y, z, _*) = arr1
println(x, y, z) //(1, 2, 3)
//2.
var list1 = (0 to 10).toList
//思路一:通过List()实现
val List(a, b, _*) = list1
//思路二:通过关键字实现
val c::d::tail = list1
println(a, b) //(0, 1)
println(c, d) //(0, 1)
}
}
1.7 匹配for表达式
Scala中还可以使用模式匹配来匹配for表达式,从而实现快速获取指定数据。
示例:

object ClassDemo {
def main(args:Array[String]):Unit = {
//1.
val map1 = Map("张三" -> 23, "李四" -> 24, "王五" -> 23, "赵六" -> 26, )
//2.
//思路一:通过if()语句实现
for((k, v) <- map if v == 23) println(k, v)
//思路二:通过固定值实现
for((k, 23) <- map) println(k, 23)
}
}
//(张三,23)
//(王五,23)
2. Option类型
实际开发中,在返回一些数据时,难免会遇到空指针异常(NullPointerException),遇到依次就处理依次相对来说还是比较繁琐的。所以在Scala中,我们返回某些数据时,可以返回一个Option类型的对象来封装具体的数据,从而实现有效的避免空指针异常。
格式:

示例:

object ClassDemo {
//1.定义一个两个数相除的方法,使用Option类型来封装结果
def divide(a:Int, b:Int) = {
if (b == 0) //除数为零
None
else
Some(a / b)
}
def main(args:Array[String]):Unit = {
//2.打印结果
//思路一:普通实现
val result1 = divide(10/0)
println(result1) //None
//思路二:通过模式匹配实现
result1 match {
case Some(x) => println(s"商为:${x}")
case None(x) => println("除数不能为零")
//除数不能为零
//思路三:通过getOrElse()方法实现
println(result1.getOrElse("除数不能为零"))
//除数不能为零
}
}
}
3. 偏函数
偏函数指被包在花括号内没有match的一组case语句,它时PartialFunction[A, B]类型的一个实例对象,其中A代表输入参数类型,B代表返回结果类型
偏函数提供了更简洁的语法,可以简化函数的定义。配合集合的函数式编程,可以让代码更简洁。
格式:
val 对象名 = { //这对大括号及其内部的一组case语句,组成了一个偏函数
case 值1 => 表达式1
case 值2 => 表达式2
case 值3 => 表达式3
...
}
示例一:入门案例

object ClassDemo {
def main(args:Array[String]):Unit = {
//1.定义偏函数
val pf:PartialFunction[Int,String] = {
case 1 => "一"
case 2 => "二"
case 3 => "三"
case _ => "未匹配"
}
//2.调用偏函数,打印结果
println(pf(1)) //一
println(pf(2)) //二
println(pf(3)) //三
println(pf(4)) //未匹配
}
}
示例二:结合map函数使用

object ClassDemo {
def main(args:Array[String]):Unit = {
//1.定义一个列表
val list1 = List(1 to 10).toList
//2.转换
val list2 = list1.map{
case x if x >= 1 && x <= 3 => "[1-3]"
case x if x >= 4 && x <= 8 => "[4-8]"
case _ => "(8-*]"
//3.打印结果
println(list2) //List([1-3], [1-3], [1-3], [4-8], [4-8], [4-8], [4-8], [4-8],(8-*], (8-*])
}
}
}
4. 正则表达式
正则表达式指正确的,符合特定规则的式子,它是一门独立的语言,并能被兼容到绝大多数的编程语言中。在Scala中,可以方便地使用正则表达式来匹配数据,具体如下:
- Scala 提供了
Regex 类定义正则表达式 - 要构造一个 Regex 对象直接使用
String 类的 r 方法即可 - 建议使用三个双引号表示正则表达式,不需要对其中的反斜杠转义
格式:
val 正则对象名 = """具体的正则表达式""".r
注意:
使用findAllMatchIn方法可以获取到所有正则匹配到的数据(字符串)
示例一:校验邮箱是否合法

object ClassDemo {
def main(args:Array[String]):Unit = {
//1.定义一个字符串,表示邮箱
val email = "qq123@163.com"
//2.定义一个正则表达式,来匹配邮箱是否合法
/*
.表示任意字符
+表示前边的内容至少出现一次
@表示这里必须是@号
\.表示转义,取消.的特殊功能,把它当作普通符合
*/
val regex = """.+@.+\..+""".r
//3.打印结果
/*
regex表示正则对象
email表示要被校验的邮箱
(regex.findAllMatchIn(email)表示从email字符串中,获取所有满足regex规则的字符串
*/
if(regex.findAllMatchIn(email).size != 0){
//走到这里,说明是合法的邮箱
println("合法")
} else {
println("非法")
}
}
}
//合法
示例二:过滤所有不合法邮箱

object ClassDemo {
def main(args:Array[String]):Unit = {
//1.定义列表,记录所有的邮箱
val emailList = List("38123845@qq.com","alda88123@gmail.com","zhansan@163.com","123afadff.com")
//2.定义一个正则表达式,来校验邮箱
/*
.表示任意字符
+表示前边的内容至少出现一次
@表示这里必须是@号
\.表示转义,取消.的特殊功能,把它当作普通符合
*/
val regex = """.+@.+\..+""".r
//3.通过filter方法,用来过滤所有不合法的邮箱
//val filterList = emailList.filter(x => regex.findAllMatchIn(x).size == 0
//简化:
val filterList = emailList.filter(regex.findAllMatchIn(_).size == 0
//4.打印结果
println(filterList)
}
}
//List("38123845@qq.com","alda88123@gmail.com","zhansan@163.com")
示例三:获取邮箱运营商

object ClassDemo {
def main(args:Array[String]):Unit = {
//1.定义列表,记录所有的邮箱
val emailList = List("38123845@qq.com","alda88123@gmail.com","zhansan@163.com","123afadff.com")
val regex = """.+@(.+)\..+""".r
//3.通过模式匹配,获取邮箱的运营商
val list2 = emailList.map{
case x @ regex(company) => x -> company
case x => x -> "未匹配"
}
//4.打印结果
println(list2)
}
}
//List((38123845@qq.com,qq),(alda88123@gmail.com,gmail),(zhansan@163.com,163),(123afadff.com,未匹配))
5. 异常处理
Scala中有两种异常处理形式:捕获异常(该方式处理完异常后,程序会继续执行)和抛出异常(该方式处理完异常后,程序会终止执行)
5.1 捕获异常
格式:
try {
// 可能会出现问题的代码
}
catch {
case ex:异常类型1 => //代码
case ex:异常类型2 => //代码
}
finally {
//代码
}
注意:
- try中的代码是我们编写的业务处理代码
- catch中表示当某个异常时,需要执行的代码
- finally中写的是不管是否出现异常都会执行的代码(比如资源释放)
5.2 抛出异常
格式:
throw new Exception("这里写异常的描述信息")
示例:

object ClassDemo {
def main(args:Array[String]):Unit = {
//1.通过try.catch来处理 除数为零异常
try{
//可能出现问题的代码
val i = 10 / 0
} catch {
//出现问题后的解决方案
//case ex:ArithmeticException => println("算数异常")
case ex:Exception => ex.printStackTrace() //将异常类型,异常的描述信息,以及异常出现的位置打印到控制台上
} finally {
//这里用来释放资源
println("释放资源")
}
//2.在main方法中抛出一个异常
throw new Exception("新异常")
println("hi") //不会被执行
}
}
//算数异常
//释放资源
6. 提取器(Extractor)
不是所有的类都像样例类一样能进行某些模式匹配。因此,一个类要想支持模式匹配,必须实现一个提取器。
注意:
- 提取器指的就是unapply()方法
- 样例类自动实现了apply()、unapply()方法,无需我们手动定义
格式:
要实现一个类的提取器,只需要在该类的伴生对象中实现一个unapply方法即可。
def unapply(stu:Student):Option[(类型1,类型2,类型3...)] = {
if(stu != null){
Some((变量1,变量2,变量3...))
}
else {
None
}
}

示例:

object ClassDemo {
//1.创建一个Student类,包含姓名和年龄两个字段
class Student(var name:String, var age:Int)
//2.实现一个类的提取器,并使用match表达式进行模式匹配,提取类中的字段
object Student{
//apply() 根据给定的字段,将其封装成一个Student类型的对象
def apply(name:String, age:Int) = new Student(name, age)
//unapply() 根据传入的学生对象,获取其对应的各个属性值
def unapply(s:Student) = {
if(s == null)
None
else
Some(s.name, s.age)
}
}
def main(args:Array[String]):Unit = {
//3.创建学生类的对象
//方式一:普通写法
val s1 = new Student("lee", 23)
//方式二:免new
val s2 = Student("mike", 24)
//4.获取对象中的各个属性值,然后打印
//方式一:普通获取
println(s1.name, s1.age) //(lee,23)
//方式二:unapply()方法
val result = Student.unapply(s1)
println(result) //Some((lee,23))
//方式三:通过模式匹配获取
s1 match {
case Student(name, age) => println(s"${name}, ${age}")
case _ => println("未匹配")
}
//lee,23
}
}
7. 案例:随机职业
需求:

import scala.io.StdIn
object ClassDemo {
def main(args:Array[String]):Unit = {
//1.提示用户录入整数,并接收
println("录入一个整数1-5:")
val num = StdIn.readInt()
//2.匹配对应的职业
val occupation = num match {
case 1 => "一品带刀侍卫"
case 2 => "宰相"
case 3 => "江湖郎中"
case 4 => "打铁匠"
case 5 => "店小二"
case 6 => "不详"
//3.打印结果
println(occupation)
}
}
本文详细介绍了Scala中的模式匹配机制,包括简单模式匹配、类型匹配、守卫、匹配样例类、匹配集合等,以及Option类型、偏函数、正则表达式和异常处理。通过实例展示了如何在不同场景下运用这些特性,如处理字符串、类型检查、数据提取、异常捕获等。同时,还探讨了如何实现类的提取器以支持模式匹配,并提供了一个随机职业匹配的案例。

453

被折叠的 条评论
为什么被折叠?



