样本类 case class
类前加上case修饰符,编译器会添加额外的语法,更好的支持模式匹配
- 添加与类名称相同的工厂方法
- 样本类参数列表中的所有参数,自动添加前缀
val
- 添加
toString, hashCode,equals
方法 - 添加
copy
方法,方便进行复制,同时修改部分参数(默认参数和命名形参结合)
//定义一个表达式和一元运算
abstract class Expr
case class UnOp(operator: String, arg: Expr) extends Expr
反编译Expr.class,UnOp.class和UnOp$.class,查看具体添加的方法
模式匹配 Pattern Match
Scala中的匹配表达式(match expression)类似于Java的switch,不过匹配一个之后就会返回(等同于隐式添加了break),如果都没有匹配到抛出MatchError
异常
不过Scala先写选择表达式
//Scala
selector match { alternatives }
//Java
switch (selector) { alternatives }
模式匹配包含一序列的选项,开头是case
,每个选项包含一个模式,中间是箭头分隔,右侧是一个或多个表达式,按代码的先后顺序比较每个模式
case pattern => expressions
模式的种类
通配符模式 wildcard pattern
_
匹配所有值,但没有变量引用匹配到的值,用来忽略对象中不关心的部分
expr match {
case BinOp(_, _, _) => println(expr + " is a binary operation")
case _ => // 处理默认情况,返回值为unit值的()
}
常量模式 constant pattern
只匹配自身,任何字面量都可以做常量,val
和单例对象也可以做常量
变量模式 variable pattern
类似于通配符匹配,匹配任何对象,在case语言的右侧,变量可以引用匹配到的值
区分:以小写字母开始的名称被当做是变量模式,否则当做常量
可以使用反引号包住变量名称,它将会被理解为常量
val v42 = 42
Some(3) match {
case Some(`v42`) => println("42") //不用反引号,理解为变量,一直输出42
case _ => println("Not 42") //输出
}
构造器模式 constructor pattern
构造器的参数也可以是模式(深度匹配)
case BinOp("+", e, Number(0)) => println("a deep match") //三层匹配
序列模式 Sequence pattern
匹配序列类型List,Array等,_*
作为模式的最后一个元素,匹配任意数量的元素
case List(0, _, _) => println("found it")//匹配从零开始的,有三个元素的列表
case List(0, _*) => println("found it")//无论任意长度,以零开头的列表
case _ =>
元组模式 Tuple pattern
case (a, b, c) => println("matched " + a + b + c)
类型模式 Type pattern
用于类型测试和类型转换
def generalSize(x: Any) = x match {
case s: String => s.length
case m: Map[_, _] => m.size//类型模式中的下划线是通配符
case _ => -1
}
Scala中的类型测试和转换故意很冗长,不推荐使用
if (x.isInstanceOf[String]) {
val s = x.asInstanceOf[String]
s.length
} else ...
由于类型擦除,不能指定泛型的类型进行匹配
case m: Map[Int, Int] => true //无论什么类型都是true
变量绑定 Variable binding
除了变量模式之外,还可以将变量添加到任何其他模式
变量名称 @ 模式
, 变量绑定模式,如果模式成功,该变量设置为匹配的对象
case UnOp("abs", e @ UnOp("abs", _)) => e
模式守卫 pattern guard
模式守卫接在模式之后,以if
开头,之后任意的布尔表达式,为真时,匹配才会成功
case BinOp("+", x, y) if x == y =>
BinOp("*", x, Number(2))
密封类 sealed class
目的是让Scala编译器来检测匹配表达式中缺失的模式组合
通过密封样本类的超类,密封的类不能添加任何新的子类,除了同一个文件中的子类。这样编译器可以确认子类,会通过警告信息表明缺少的模式组合
sealed abstract class Expr
case class Var(name: String) extends Expr
case class Number(num: Double) extends Expr
case class UnOp(operator: String, arg: Expr) extends Expr
case class BinOp(operator: String, left: Expr, right: Expr) extends Expr
//产生警告
def describe(e: Expr): String = e match {
case Number(_) => "a number"
case Var(_) => "a variable"
}
//通过注解抑制警告
def describe(e: Expr): String = (e: @unchecked) match {
case Number(_) => "a number"
case Var(_) => "a variable"
}
Option类型
Scala定义了一个名为Option
的标准类型用于表示可选值。这样的值可以有两种形式:Some(x)
,其中x是实际值,或者是None
对象,表示缺失的值。
Scala集合中的一些标准操作会产生可选值,例如Scala的Map的get方法会生成Some(x)
或返回None。
def show(x: Option[String]) = x match {
case Some(s) => s
case None => "?"
}
使用PartialFunction
花括号中的case序列可以当做广义的函数字面量,正常的函数字面量只有一个入口点和参数列表,case序列可以有多个入口点,每个case语句就是一个入口点,对应有自己的参数列表
val withDefault: Option[Int] => Int = {
case Some(x) => x
case None => 0
}
该例子中有两个case,对应两个入口点
case序列写作偏函数,可以避免匹配错误的情况
val second: PartialFunction[List[Int],Int] = {
case x :: y :: _ => y
}
以上函数会被翻译为
new PartialFunction[List[Int], Int] {
def apply(xs: List[Int]) = xs match {
case x :: y :: _ => y
}
def isDefinedAt(xs: List[Int]) = xs match {
case x :: y :: _ => true
case _ => false
}
}
偏函数中的isDefinedAt
方法,用来测试该函数对特定的值是否可用,在调用真正函数之前会先进行测试