Scala_06【案例类和模式匹配】
主要内容
- 案例类
- 模式匹配
学习目标
- 了解案例类的特点和使用场景
- 掌握案例类的用法
- 掌握模式匹配的语法和应用
案例类和模式匹配
1. 案例类(Case classes)
- 案例类(一些资料译作样例类)和普通类差不多,只有几点关键差别,接下来的介绍将会涵盖这些差别。案例类非常适合用于不可变的数据。后面我们将会介绍他们在模式匹配中的应用。
1.1 定义一个案例类
- 语法:
一个最简单的案例类定义由关键字case class,类名,参数列表(可为空)组成:
case class Laptop(cpu: String, gpu: String, ram: String)
val laptop = Laptop("i9 11990k", "RTX 3080Ti", "128G DDR5")
-
注意在实例化案例类Laptop时,并没有使用关键字new,这是因为案例类有一个默认的apply方法来负责对象的创建。
-
当你创建包含参数的案例类时,这些参数是公开(public)的val,即参数作为对象的成员属性。
println(laptop.cpu) // i9 11990k
laptop.cpu="奔腾" // 编译报错
我们不能给laptop.cpu重新赋值,因为它是一个val(不可变)。在案例类中使用var也是可以的,但不推荐。
1.2 相等关系比较
- 案例类在比较的时候是按值比较而非按引用比较:
case class Laptop(cpu: String, gpu: String, ram: String)
val laptop1 = Laptop("i9 11990k", "RTX 3080Ti", "128G DDR5")
val laptop2 = Laptop("i9 11990k", "RTX 3080Ti", "128G DDR5")
println(laptop1 == laptop2) // true
- Scala会将JVM中成员属性值相同的样例对象优化的保存一份
- 尽管laptop1和laptop2看起来是创建了两个对象,但他们其实是全等的。
1.3 对象的复制
- 我们可以通过
copy
方法创建一个案例类实例的浅拷贝,同时可以指定构造参数来做一些改变。
case class Laptop(cpu: String, gpu: String, ram: String)
val laptop1 = Laptop("i9 11990k", "RTX 3080Ti", "128G DDR5")
println(laptop1) // Laptop(i9 11990k,RTX 3080Ti,128G DDR5)
println(laptop1.copy(cpu = "奔腾3")) //Laptop(奔腾3,RTX 3080Ti,128G DDR5)
2. 模式匹配(Pattern matching)
- 语法:
一个模式匹配语句包括一个待匹配的值,match
关键字,以及至少一个case
语句。
2.1 值匹配
import scala.util.Random
val x: Int = Random.nextInt(10)
x match {
case 0 => "zero"
case 1 => "one"
case 2 => "two"
case _ => "other"
}
-
案例中的
val x
是一个0到10之间的随机整数,将它放在match
运算符的左侧对其进行模式匹配,match
的右侧是包含4条case
的表达式,其中最后一个case _
表示匹配其余所有情况,在这里就是其他可能的整型值(类似Java switch中的default
)。 -
match
表达式同样具有返回值
def matchTest(x: Int): String = {
x match {
case 0 => "zero"
case 1 => "one"
case 2 => "two"
case _ => "other"
}
}
println(matchTest(1)) // one
println(matchTest(2)) // two
println(matchTest(10)) // other
- 这个match表达式是String类型的,因为所有的情况(case)均返回String,所以matchTest函数的返回值是String类型。
2.2 类型匹配(Matching on type)
- match语句可以直接匹配对象的真实类型
def matchTest(x: Any) {
x match {
case x: Int => println("This is a Int.")
case x: String => println("This is a String.")
case x: Double => println("This is a Double.")
case _ => println("Something else.")
}
}
matchTest(1) // This is a Int.
matchTest("1") // This is a String.
matchTest(1.0) // This is a Double.
matchTest(1.0F) // Something else.
2.3 模式守卫(Pattern guards)
- match中的case语句可以在变量后添加类似for推导式中的守卫语法,用来丰富匹配的条件。
def matchTest(x: Int) {
x match {
case x if x >= 85 => println("A")
case x if x >= 75 => println("B")
case x if x >= 60 => println("C")
case _ => println("D")
}
}
matchTest(90) // A
matchTest(80) // B
matchTest(70) // C
matchTest(59) // D
2.4 案例类的匹配
- 案例类有默认的unapply方法来负责对象的匹配和属性的提取。
abstract class Device
case class SmartPhone(brand: String, netType: String) extends Device
case class Computer(brand: String, diskSize: String) extends Device
- Device是一个虚基类,他有两个实现类SmartPhone,Computer都是案例类。
def matchTest(device: Device): Unit = {
device match {
case SmartPhone("SAMSUNG", _) => println(s"This is a SAMSUNG phone.")
case SmartPhone(brand, netType) => println(s"This is a $brand $netType phone.")
case Computer(brand, "1TB") => println(s"This is a $brand computer with 1TB disk. ")
}
}
val phone1 = SmartPhone("SAMSUNG", "5G")
val phone2 = SmartPhone("HUAWEI", "5G")
val computer = Computer("ALIENWARE", "1TB")
matchTest(phone1) // This is a SAMSUNG phone.
matchTest(phone2) // This is a HUAWEI 5G phone.
matchTest(computer) // This is a ALIENWARE computer with 1TB disk.
- matchTest函数接受一个抽象类Device对象作为输入参数,然后匹配其具体类型。
- 首先判断对象类型是否一致,如果一致则匹配后面的参数进行属性的提取,其中
_
表示通配占位符,而具体的属性值进行等值匹配。
2.5 密封类(Sealed classes)
-
特质(trait)和类(class)可以用
sealed
关键字进行修饰,标记为密封的,密封类的所有子类都必须与之定义在相同文件中,从而保证所有子类型都是已知的。 -
密封类可以避免进行不必要的其他情况的匹配。