一、match基本介绍Scala中的模式匹配类似于Java中的switch语法但是更加强大。模式匹配语法中采用match关键字声明每个分支采用case关键字进行声明当需要匹配时会从第一个case分支开始如果匹配成功那么执行对应的逻辑代码如果匹配不成功继续执行下一个分支进行判断。如果所有case都不匹配那么会执行case _分支类似于Java中default语句。1.1 Java Switch的简单回顾// Javainti1;switch(i){case0:break;case1:break;default:break}1.2 Scala的模式匹配// 模式匹配类似于Java的switch语法valoper#valn120valn210varres0opermatch{caseresn1n2case-resn1-n2case*resn1*n2case/resn1/n2case_println(oper error)}println(resres)1.3 match的细节和注意事项如果所有case都不匹配那么会执行case _分支类似于Java中default语句如果所有case都不匹配又没有写case _分支那么会抛出MatchError每个case中不用break语句自动中断case可以在match中使用其它类型而不仅仅是字符等价于 java switch 的:后面的代码块到下一个 case是作为一个整体执行可以使用{}扩起来也可以不扩。valoper1valn120valn210varres0opermatch{caseresn1n2case1resn1/n2case_println(oper error)}println(resres)二、守卫2.1 基本介绍如果想要表达匹配某个范围的数据就需要在模式匹配中增加条件守卫。2.2 应用案例for(ch--3!){varsign0vardigit0chmatch{casesign1case-sign-1// 说明..case_ifch.toString.equals(3)digit3case_sign2}println(ch sign digit)}2.3 课堂思考题如看下面的代码会输出什么代码1for(ch--3!){varsign0vardigit0chmatch{casesign1case-sign-1// 说明..case_digit3case_sign2}println(ch sign digit)}代码2for(ch--3!){varsign0vardigit0chmatch{case_digit3casesign1case-sign-1// 说明..}println(ch sign digit)}三、模式中的变量3.1 基本介绍如果在case关键字后跟变量名那么match前表达式的值会赋给那个变量。3.2 应用案例valchVchmatch{caseprintln(ok~)casemycharprintln(ok~mychar)case_println(ok~~)}四、类型匹配4.1 基本介绍可以匹配对象的任意类型这样做避免了使用isInstanceOf和asInstanceOf方法。4.2 应用案例// 类型匹配, obj 可能有如下的类型vala7valobjif(a1)1elseif(a2)2elseif(a3)BigInt(3)elseif(a4)Map(aa-1)elseif(a5)Map(1-aa)elseif(a6)Array(1,2,3)elseif(a7)Array(aa,1)elseif(a8)Array(aa)valresultobjmatch{casea:Intacaseb:Map[String,Int]对象是一个字符串-数字的Map集合casec:Map[Int,String]对象是一个数字-字符串的Map集合cased:Array[String]对象是一个字符串数组casee:Array[Int]对象是一个数字数组casef:BigIntInt.MaxValuecase_啥也不是}println(result)4.3 类型匹配注意事项Map[String, Int]和Map[Int, String]是两种不同的类型其它类推。在进行类型匹配时编译器会预先检测是否有可能的匹配如果没有则报错。case i : Int i表示将i obj(其它类推)然后再判断类型如果case _出现在match中间则表示隐藏变量名即不使用而不是表示默认匹配。valresultobjmatch{casea:Intacase_:BigIntInt.MaxValue//看这里!caseb:Map[String,Int]对象是一个字符串-数字的Map集合casec:Map[Int,String]对象是一个数字-字符串的Map集合cased:Array[String]对象是一个字符串数组casee:Array[Int]对象是一个数字数组case_啥也不是}println(result)五、匹配数组5.1 基本介绍Array(0)匹配只有一个元素且为0的数组。Array(x,y)匹配数组有两个元素并将两个元素赋值为x和y。当然可以依次类推Array(x,y,z)匹配数组有3个元素的等等…Array(0,_*)匹配数组以0开始5.2 应用案例for(arr-Array(Array(0),Array(1,0),Array(0,1,0),Array(1,1,0),Array(1,1,0,1))){valresultarrmatch{caseArray(0)0caseArray(x,y)xycaseArray(0,_*)以0开头和数组case_什么集合都不是}println(result result)}说明通过增加和删除for循环的数组来看代码运行的结果加强对匹配数组的理解。六、匹配列表6.1 应用案例for(list-Array(List(0),List(1,0),List(0,0,0),List(1,0,0))){valresultlistmatch{case0::Nil0//casex::y::Nilx y//case0::tail0 ...//case_something else}println(result)}思考题如果要匹配List(88)这样的只含有一个元素的列表并原值返回应该怎么写七、匹配元组7.1 应用案例// 元组匹配for(pair-Array((0,1),(1,0),(1,1),(1,0,2))){valresultpairmatch{//case(0,_)0 ...//case(y,0)y//case_other//.}println(result)}思考如果要匹配(10, 30)这样任意两个元素的对偶元组应该如何写八、对象匹配8.1 基本介绍对象匹配什么才算是匹配呢规则如下case中对象的unapply方法(对象提取器)返回Some集合则为匹配成功返回none集合则为匹配失败8.2 应用案例1objectSquare{defunapply(z:Double):Option[Double]Some(math.sqrt(z))defapply(z:Double):Doublez*z}// 模式匹配使用valnumber:Double36.0numbermatch{caseSquare(n)println(n)case_println(nothing matched)}8.3 应用案例1的小结构建对象时apply会被调用比如val n1 Square(5)当将Square(n)写在 case 后时[case Square(n) xxx]会默认调用unapply方法(对象提取器)number 会被传递给def unapply(z: Double)的 z 形参如果返回的是Some集合则unapply提取器返回的结果会返回给 n 这个形参case中对象的unapply方法(提取器)返回some集合则为匹配成功返回none集合则为匹配失败8.4 应用案例2objectNames{defunapplySeq(str:String):Option[Seq[String]]{if(str.contains(,))Some(str.split(,))elseNone}}valnamesStringAlice,Bob,Thomas//说明namesStringmatch{caseNames(first,second,third){println(the string contains three peoples names)// 打印字符串println(s$first$second$third)}case_println(nothing matched)}8.5 应用案例2的小结当case后面的对象提取器方法的参数为多个则会默认调用def unapplySeq()方法如果unapplySeq返回是Some获取其中的值判断得到的sequence中的元素的个数是否是三个如果是三个则把三个元素分别取出赋值给firstsecond和third其它的规则不变。九、变量声明中的模式9.1 基本介绍match中每一个case都可以单独提取出来意思是一样的。9.2 应用案例val(x,y)(1,2)val(q,r)BigInt(10)/%3//说明 q BigInt(10) / 3 r BigInt(10) % 3valarrArray(1,7,2,9)valArray(first,second,_*)arr// 提出arr的前两个元素println(first,second)十、for表达式中的模式10.1 基本介绍for循环也可以进行模式匹配。10.2 应用案例valmapMap(A-1,B-0,C-3)for((k,v)-map){println(k - v)}//说明for((k,0)-map){println(k -- 0)}//说明for((k,v)-mapifv0){println(k --- v)}十一、样例类11.1 样例类快速入门abstractclassAmountcaseclassDollar(value:Double)extendsAmountcaseclassCurrency(value:Double,unit:String)extendsAmountcaseobjectNoAmountextendsAmount说明: 这里的 DollarCurrency, NoAmount 是样例类。11.2 基本介绍样例类仍然是类样例类用case关键字进行声明。样例类是为模式匹配而优化的类构造器中的每一个参数都成为val——除非它被显式地声明为var不建议这样做在样例类对应的伴生对象中提供apply方法让你不用new关键字就能构造出相应的对象提供unapply方法让模式匹配可以工作将自动生成toString、equals、hashCode和copy方法有点类似模板类直接给生成供程序员使用除上述外样例类和其他类完全一样。你可以添加方法和字段扩展它们11.3 样例类最佳实践1当我们有一个类型为Amount的对象时可以用模式匹配来匹配他的类型并将属性值绑定到变量(即把样例类对象的属性值提取到某个变量该功能有用)for(amt-Array(Dollar(1000.0),Currency(1000.0,RMB),NoAmount)){valresultamtmatch{//说明caseDollar(v)$v//说明caseCurrency(v,u)v ucaseNoAmount}println(amt: result)}11.4 样例类最佳实践2样例类的copy方法和带名参数copy创建一个与现有对象值相同的新对象并可以通过带名参数来修改某些属性。valamtCurrency(29.95,RMB)valamt1amt.copy()//创建了一个新的对象但是属性值一样valamt2amt.copy(value19.95)//创建了一个新对象但是修改了货币单位valamt3amt.copy(unit英镑)//..println(amt)println(amt2)println(amt3)十二、case语句的中置(缀)表达式12.1 基本介绍什么是中置表达式1 2这就是一个中置表达式。如果unapply方法产出一个元组你可以在case语句中使用中置表示法。比如可以匹配一个List序列。12.2 应用实例List(1,3,5,9)match{//修改并测试//1.两个元素间::叫中置表达式,至少firstsecond两个匹配才行.//2.first 匹配第一个 second 匹配第二个, rest 匹配剩余部分(5,9)casefirst::second::restprintln(firstsecondrest.length)//case_println(匹配不到...)}十三、匹配嵌套结构13.1 基本介绍操作原理类似于正则表达式13.2 最佳实践案例-商品捆绑打折出售现在有一些商品请使用Scala设计相关的样例类完成商品捆绑打折出售。要求商品捆绑可以是单个商品也可以是多个商品。打折时按照折扣x元进行设计。能够统计出所有捆绑商品打折后的最终价格13.3 创建样例类abstractclassItem// 项caseclassBook(description:String,price:Double)extendsItem//Bundle 捆 discount: Double 折扣 item: Item*caseclassBundle(description:String,discount:Double,item:Item*)extendsItem13.4 匹配嵌套结构(就是Bundle的对象)//给出案例表示有一捆数单本漫画40-10文学作品(两本书)8030-20 30 90 120.0valsaleBundle(书籍,10,Book(漫画,40),Bundle(文学作品,20,Book(《阳关》,80),Book(《围城》,30)))13.5 知识点1-将descr绑定到第一个Book的描述valsaleBundle(书籍,10,Book(漫画,40),Bundle(文学作品,20,Book(《阳关》,80),Book(《围城》,30)))valressalematch{//如果我们进行对象匹配时不想接受某些值则使用_ 忽略即可_* 表示所有caseBundle(_,_,Book(desc,_),_*)desc}13.6 知识点2-通过表示法将嵌套的值绑定到变量valsaleBundle(书籍,10,Book(漫画,40),Bundle(文学作品,20,Book(《阳关》,80),Book(《围城》,30)))valresult2salematch{caseBundle(_,_,art Book(_,_),rest _*)(art,rest)}println(result2)println(art result2._1)println(restresult2._2)13.7 知识点3-不使用_*绑定剩余Item到restvalsaleBundle(书籍,10,Article(漫画,40),Bundle(文学作品,20,Article(《阳关》,80),Article(《围城》,30)))valresult2salematch{//说明因为没有使用 _* 即明确说明没有多个Bundle,所以返回的rest就不是WrappedArray了。caseBundle(_,_,art Book(_,_),rest)(art,rest)}println(result2)println(art result2._1)println(restresult2._2)13.8 最佳实践案例-商品捆绑打折出售完整实现defprice(it:Item):Double{itmatch{caseBook(_,p)p//生成一个新的集合,_是将its中每个循环的元素传递到price中it中。递归操作,分析一个简单的流程caseBundle(_,disc,its _*)its.map(price _).sum-disc}}十四、密封类14.1 基本介绍如果想让case类的所有子类都必须在申明该类的相同的源文件中定义可以将样例类的通用超类声明为sealed这个超类称之为密封类。密封就是不能在其他文件中定义子类。14.2 案例演示abstractsealedclassAmountcaseclassDollar(value:Double)extendsAmountcaseclassCurrency(value:Double,unit:String)extendsAmountcaseobjectNothingextendsAmount当Amount没有声明为sealed时在Temp.scala中可以定义样例类 Dollar2当Amount声明为sealed时在Temp.scala中不能定义样例类 Dollar2提示错误信息: illegal inheritance from sealed class Amount
Scala核心编程(十二)模式匹配
发布时间:2026/5/31 13:56:16
一、match基本介绍Scala中的模式匹配类似于Java中的switch语法但是更加强大。模式匹配语法中采用match关键字声明每个分支采用case关键字进行声明当需要匹配时会从第一个case分支开始如果匹配成功那么执行对应的逻辑代码如果匹配不成功继续执行下一个分支进行判断。如果所有case都不匹配那么会执行case _分支类似于Java中default语句。1.1 Java Switch的简单回顾// Javainti1;switch(i){case0:break;case1:break;default:break}1.2 Scala的模式匹配// 模式匹配类似于Java的switch语法valoper#valn120valn210varres0opermatch{caseresn1n2case-resn1-n2case*resn1*n2case/resn1/n2case_println(oper error)}println(resres)1.3 match的细节和注意事项如果所有case都不匹配那么会执行case _分支类似于Java中default语句如果所有case都不匹配又没有写case _分支那么会抛出MatchError每个case中不用break语句自动中断case可以在match中使用其它类型而不仅仅是字符等价于 java switch 的:后面的代码块到下一个 case是作为一个整体执行可以使用{}扩起来也可以不扩。valoper1valn120valn210varres0opermatch{caseresn1n2case1resn1/n2case_println(oper error)}println(resres)二、守卫2.1 基本介绍如果想要表达匹配某个范围的数据就需要在模式匹配中增加条件守卫。2.2 应用案例for(ch--3!){varsign0vardigit0chmatch{casesign1case-sign-1// 说明..case_ifch.toString.equals(3)digit3case_sign2}println(ch sign digit)}2.3 课堂思考题如看下面的代码会输出什么代码1for(ch--3!){varsign0vardigit0chmatch{casesign1case-sign-1// 说明..case_digit3case_sign2}println(ch sign digit)}代码2for(ch--3!){varsign0vardigit0chmatch{case_digit3casesign1case-sign-1// 说明..}println(ch sign digit)}三、模式中的变量3.1 基本介绍如果在case关键字后跟变量名那么match前表达式的值会赋给那个变量。3.2 应用案例valchVchmatch{caseprintln(ok~)casemycharprintln(ok~mychar)case_println(ok~~)}四、类型匹配4.1 基本介绍可以匹配对象的任意类型这样做避免了使用isInstanceOf和asInstanceOf方法。4.2 应用案例// 类型匹配, obj 可能有如下的类型vala7valobjif(a1)1elseif(a2)2elseif(a3)BigInt(3)elseif(a4)Map(aa-1)elseif(a5)Map(1-aa)elseif(a6)Array(1,2,3)elseif(a7)Array(aa,1)elseif(a8)Array(aa)valresultobjmatch{casea:Intacaseb:Map[String,Int]对象是一个字符串-数字的Map集合casec:Map[Int,String]对象是一个数字-字符串的Map集合cased:Array[String]对象是一个字符串数组casee:Array[Int]对象是一个数字数组casef:BigIntInt.MaxValuecase_啥也不是}println(result)4.3 类型匹配注意事项Map[String, Int]和Map[Int, String]是两种不同的类型其它类推。在进行类型匹配时编译器会预先检测是否有可能的匹配如果没有则报错。case i : Int i表示将i obj(其它类推)然后再判断类型如果case _出现在match中间则表示隐藏变量名即不使用而不是表示默认匹配。valresultobjmatch{casea:Intacase_:BigIntInt.MaxValue//看这里!caseb:Map[String,Int]对象是一个字符串-数字的Map集合casec:Map[Int,String]对象是一个数字-字符串的Map集合cased:Array[String]对象是一个字符串数组casee:Array[Int]对象是一个数字数组case_啥也不是}println(result)五、匹配数组5.1 基本介绍Array(0)匹配只有一个元素且为0的数组。Array(x,y)匹配数组有两个元素并将两个元素赋值为x和y。当然可以依次类推Array(x,y,z)匹配数组有3个元素的等等…Array(0,_*)匹配数组以0开始5.2 应用案例for(arr-Array(Array(0),Array(1,0),Array(0,1,0),Array(1,1,0),Array(1,1,0,1))){valresultarrmatch{caseArray(0)0caseArray(x,y)xycaseArray(0,_*)以0开头和数组case_什么集合都不是}println(result result)}说明通过增加和删除for循环的数组来看代码运行的结果加强对匹配数组的理解。六、匹配列表6.1 应用案例for(list-Array(List(0),List(1,0),List(0,0,0),List(1,0,0))){valresultlistmatch{case0::Nil0//casex::y::Nilx y//case0::tail0 ...//case_something else}println(result)}思考题如果要匹配List(88)这样的只含有一个元素的列表并原值返回应该怎么写七、匹配元组7.1 应用案例// 元组匹配for(pair-Array((0,1),(1,0),(1,1),(1,0,2))){valresultpairmatch{//case(0,_)0 ...//case(y,0)y//case_other//.}println(result)}思考如果要匹配(10, 30)这样任意两个元素的对偶元组应该如何写八、对象匹配8.1 基本介绍对象匹配什么才算是匹配呢规则如下case中对象的unapply方法(对象提取器)返回Some集合则为匹配成功返回none集合则为匹配失败8.2 应用案例1objectSquare{defunapply(z:Double):Option[Double]Some(math.sqrt(z))defapply(z:Double):Doublez*z}// 模式匹配使用valnumber:Double36.0numbermatch{caseSquare(n)println(n)case_println(nothing matched)}8.3 应用案例1的小结构建对象时apply会被调用比如val n1 Square(5)当将Square(n)写在 case 后时[case Square(n) xxx]会默认调用unapply方法(对象提取器)number 会被传递给def unapply(z: Double)的 z 形参如果返回的是Some集合则unapply提取器返回的结果会返回给 n 这个形参case中对象的unapply方法(提取器)返回some集合则为匹配成功返回none集合则为匹配失败8.4 应用案例2objectNames{defunapplySeq(str:String):Option[Seq[String]]{if(str.contains(,))Some(str.split(,))elseNone}}valnamesStringAlice,Bob,Thomas//说明namesStringmatch{caseNames(first,second,third){println(the string contains three peoples names)// 打印字符串println(s$first$second$third)}case_println(nothing matched)}8.5 应用案例2的小结当case后面的对象提取器方法的参数为多个则会默认调用def unapplySeq()方法如果unapplySeq返回是Some获取其中的值判断得到的sequence中的元素的个数是否是三个如果是三个则把三个元素分别取出赋值给firstsecond和third其它的规则不变。九、变量声明中的模式9.1 基本介绍match中每一个case都可以单独提取出来意思是一样的。9.2 应用案例val(x,y)(1,2)val(q,r)BigInt(10)/%3//说明 q BigInt(10) / 3 r BigInt(10) % 3valarrArray(1,7,2,9)valArray(first,second,_*)arr// 提出arr的前两个元素println(first,second)十、for表达式中的模式10.1 基本介绍for循环也可以进行模式匹配。10.2 应用案例valmapMap(A-1,B-0,C-3)for((k,v)-map){println(k - v)}//说明for((k,0)-map){println(k -- 0)}//说明for((k,v)-mapifv0){println(k --- v)}十一、样例类11.1 样例类快速入门abstractclassAmountcaseclassDollar(value:Double)extendsAmountcaseclassCurrency(value:Double,unit:String)extendsAmountcaseobjectNoAmountextendsAmount说明: 这里的 DollarCurrency, NoAmount 是样例类。11.2 基本介绍样例类仍然是类样例类用case关键字进行声明。样例类是为模式匹配而优化的类构造器中的每一个参数都成为val——除非它被显式地声明为var不建议这样做在样例类对应的伴生对象中提供apply方法让你不用new关键字就能构造出相应的对象提供unapply方法让模式匹配可以工作将自动生成toString、equals、hashCode和copy方法有点类似模板类直接给生成供程序员使用除上述外样例类和其他类完全一样。你可以添加方法和字段扩展它们11.3 样例类最佳实践1当我们有一个类型为Amount的对象时可以用模式匹配来匹配他的类型并将属性值绑定到变量(即把样例类对象的属性值提取到某个变量该功能有用)for(amt-Array(Dollar(1000.0),Currency(1000.0,RMB),NoAmount)){valresultamtmatch{//说明caseDollar(v)$v//说明caseCurrency(v,u)v ucaseNoAmount}println(amt: result)}11.4 样例类最佳实践2样例类的copy方法和带名参数copy创建一个与现有对象值相同的新对象并可以通过带名参数来修改某些属性。valamtCurrency(29.95,RMB)valamt1amt.copy()//创建了一个新的对象但是属性值一样valamt2amt.copy(value19.95)//创建了一个新对象但是修改了货币单位valamt3amt.copy(unit英镑)//..println(amt)println(amt2)println(amt3)十二、case语句的中置(缀)表达式12.1 基本介绍什么是中置表达式1 2这就是一个中置表达式。如果unapply方法产出一个元组你可以在case语句中使用中置表示法。比如可以匹配一个List序列。12.2 应用实例List(1,3,5,9)match{//修改并测试//1.两个元素间::叫中置表达式,至少firstsecond两个匹配才行.//2.first 匹配第一个 second 匹配第二个, rest 匹配剩余部分(5,9)casefirst::second::restprintln(firstsecondrest.length)//case_println(匹配不到...)}十三、匹配嵌套结构13.1 基本介绍操作原理类似于正则表达式13.2 最佳实践案例-商品捆绑打折出售现在有一些商品请使用Scala设计相关的样例类完成商品捆绑打折出售。要求商品捆绑可以是单个商品也可以是多个商品。打折时按照折扣x元进行设计。能够统计出所有捆绑商品打折后的最终价格13.3 创建样例类abstractclassItem// 项caseclassBook(description:String,price:Double)extendsItem//Bundle 捆 discount: Double 折扣 item: Item*caseclassBundle(description:String,discount:Double,item:Item*)extendsItem13.4 匹配嵌套结构(就是Bundle的对象)//给出案例表示有一捆数单本漫画40-10文学作品(两本书)8030-20 30 90 120.0valsaleBundle(书籍,10,Book(漫画,40),Bundle(文学作品,20,Book(《阳关》,80),Book(《围城》,30)))13.5 知识点1-将descr绑定到第一个Book的描述valsaleBundle(书籍,10,Book(漫画,40),Bundle(文学作品,20,Book(《阳关》,80),Book(《围城》,30)))valressalematch{//如果我们进行对象匹配时不想接受某些值则使用_ 忽略即可_* 表示所有caseBundle(_,_,Book(desc,_),_*)desc}13.6 知识点2-通过表示法将嵌套的值绑定到变量valsaleBundle(书籍,10,Book(漫画,40),Bundle(文学作品,20,Book(《阳关》,80),Book(《围城》,30)))valresult2salematch{caseBundle(_,_,art Book(_,_),rest _*)(art,rest)}println(result2)println(art result2._1)println(restresult2._2)13.7 知识点3-不使用_*绑定剩余Item到restvalsaleBundle(书籍,10,Article(漫画,40),Bundle(文学作品,20,Article(《阳关》,80),Article(《围城》,30)))valresult2salematch{//说明因为没有使用 _* 即明确说明没有多个Bundle,所以返回的rest就不是WrappedArray了。caseBundle(_,_,art Book(_,_),rest)(art,rest)}println(result2)println(art result2._1)println(restresult2._2)13.8 最佳实践案例-商品捆绑打折出售完整实现defprice(it:Item):Double{itmatch{caseBook(_,p)p//生成一个新的集合,_是将its中每个循环的元素传递到price中it中。递归操作,分析一个简单的流程caseBundle(_,disc,its _*)its.map(price _).sum-disc}}十四、密封类14.1 基本介绍如果想让case类的所有子类都必须在申明该类的相同的源文件中定义可以将样例类的通用超类声明为sealed这个超类称之为密封类。密封就是不能在其他文件中定义子类。14.2 案例演示abstractsealedclassAmountcaseclassDollar(value:Double)extendsAmountcaseclassCurrency(value:Double,unit:String)extendsAmountcaseobjectNothingextendsAmount当Amount没有声明为sealed时在Temp.scala中可以定义样例类 Dollar2当Amount声明为sealed时在Temp.scala中不能定义样例类 Dollar2提示错误信息: illegal inheritance from sealed class Amount