AmosCloud

Library

Have a Question?

If you have any question you can ask below or enter what you are looking for!

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关键字进行修饰,标记为密封的,密封类的所有子类都必须与之定义在相同文件中,从而保证所有子类型都是已知的。

  • 密封类可以避免进行不必要的其他情况的匹配。