scala中的模式匹配类似与java中的switch语法,但功能比switch更加强大,且能匹配的方式各种各样。
我总结了一下,scala的match的匹配方式有如下几种:
- 通配模式
- 常量模式
- 变量模式
- 构造器模式
- 序列模式
- 元组模式
- 类型模式
在介绍模式匹配之前,先要介绍一下Scala的样本类,样本类可以在模式匹配中使用,而一般的普通类则不行。
样本类
先来一个样本类的例子:
abstract class Animal
case class Dog() extends Animal
case class Person(name: String, age: Int) extends Animal
Scala对样本类添加了一些辅助方法:
1、工厂方法
var p = new Dog()
var p = Dog()
这两句的作用是一样的。
2、样本类的参数被当成字段进行处理
var p = Person("shadon", 28)
p.name
p.age
3、默认实现了toString, hashCode和equals方法
由于以上的三点,因此在使用样本类时非常便利。
通配模式:
person match {
case Person(name, age) =>
println("是个人")
case _ =>
println("other")
}
匹配person对象,如果是2个参数的则匹配第一个,这个其实是构造器模式,后面会讲到。通配模式的重点在‘_’,表示匹配任意对象。当然通配符‘_’也可以放在类的参数上,用于匹配参数个数,例如:
p match {
case Person(_, _) =>
println("person")
case _ =>
println("other")
}
常量模式
def match1(p: Any): Unit = {
p match {
case 2 =>
println("number 2")
case "hell" =>
println("string hell")
case Nil =>
println("null")
case _ =>
println("other")
}
}
常量模式用于匹配常量值,这个好理解。
变量模式
scala认定变量模式的方式比较奇特:用小写字母开头的变量;
def match1(p: Any): String = {
val pp = Math.PI
p match {
case pp => "PI"
}
}
这里不论传入什么参数,都会被匹配,而且case里面只能使用一个,不然会抛错。如果想将变量模式转变成常量模式只需要增加转义符:`pp`。
思考:这个变量模式我一直想不通,到底有何用途,哪位网友知道请相告知,谢谢!
构造器模式
def match1(p: Any): String = {
p match {
case Person("shadon", _) => "shadon"
case _ => "no"
}
}
这里匹配第一个参数为“shadon”的Person。
序列模式
def match1(p: Any): String = {
p match {
case List(9, _, _) => "9 start"
case _ => "no"
}
}
序列模式主要用于匹配像List, Array这样的序列类。使用‘_*’可以匹配零到多个参数。
def match1(p: Any): String = {
p match {
case List(9, _, _*) => "9 start"
case _ => "no"
}
}
元组模式
def match1(p: Any): String = {
p match {
case (_, 1) => "(_, 1)"
case _ => "no"
}
}
类型模式
def match1(p: Any): String = {
p match {
case a: String => "String a = " + a
case b: Int => "Int a = " + b
case c: Boolean => "boolean c = " + c
case _ => "no"
}
}
注意在类型模式中使用泛型存在类型擦除的问题:
def match1(p: Any): String = {
p match {
case map: Map[Int, String] => "Map[Int, String]"
case _ => "no"
}
}
def main(args: Array[String]): Unit = {
println(match1(Map(1 -> 1, 2 -> 1)))
}
这里虽然指定了Map[Int, String]类型,但是结果还是能匹配上,这里任意的Map类型都可以匹配成功。泛型擦除如果出现,编译器都会提示警告信息,所以大家看到泛型擦除的警告信息一定要检查一下,以免遗留BUG。在泛型的擦除问题中,数组Array是一个例外,因为数组在Java或Scala中都进行了特殊处理。
def match1(p: Any): String = {
p match {
case map: List[String] => "List[String]"
case _ => "no"
}
}
def main(args: Array[String]): Unit = {
println(match1(List(1)))
}
这里数组的类型可以正确匹配。
最后介绍一下模式匹配中的变量绑定
变量绑定@
abstract class Animal
case class Dog(name: String, age: Int) extends Animal
case class Person(name: String, age: Animal) extends Animal
object MatchDemo {
def match1(p: Any): String = {
p match {
case Person("shadon", dog @ Dog("dog", _)) => "dog = " + dog
case _ => "no"
}
}
def main(args: Array[String]): Unit = {
println(match1(Person("shadon", Dog("dog", 1))))
}
}
用@符号设置绑定变量,相当于 val dog = Dog(“dog”, _),这样后面就可以使用dog访问Person的第二个参数。
参考资料:
- 《Scala编程–综合进阶向导》
- http://www.scala-lang.org/