本节学习样例类的相关知识。有关模式匹配的知识点击阅读: 模式匹配
一、样例类
1.基本介绍
样例类是一种特殊的类,主要用于优化模式匹配。
- 样例类仍然是类
- 样例类用case关键字进行声明。
- 样例类是为模式匹配而优化的类。
- 构造器中的每一个参数都成为val——除非它被显式地声明为var(不建议这样做)。
- 在样例类对应的伴生对象中提供apply方法,让你不用new关键字就能构造出相应的对象。
- 提供unapply方法让模式匹配可以工作。
- 将自动生成toString、equals、hashCode和copy方法(类似模板类,直接给生成,方便使用)。
- 除上述外,样例类和其他类完全一样。你可以添加方法和字段,扩展它们。
2.应用案例
背景:编写一个操作算术表达式的类库。为保持简单,我们将注意力集中在由变量、数,以及一元和二元操作符组成的算术表达式上。
用Scala的类层次结构表达:如下代码所示:
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
①样例类
类层次结构包括一个抽象的基类 Expr 和 四个子类,每一个子类都表示我们要考虑的一种表达式。五个类的定义体都是空的,Scala 允许我们省去空定义体的花括号, class C 和 class C {} 是相同的。
- case 修饰的类就是样例类(case class),所以上述案例有四个样例类
- 样例类会添加一个跟类同名的工厂方法:apply(),可以不用new关键字就可以构造新的对象。
当你需要嵌套定义时,工厂方法尤为有用。不用new关键字,可以一眼就看明白表达式的结构
- 样例类中参数列表中的每个参数都隐式的获得了一个val前缀,所以可以当做字段处理
- 样例类会自动实现toString、hashCode、equals和copy 方法。(Scala用 == 代替equlas 比较对象相等性)
你只需要多写一个 case 修饰符,就可以获得样例类带来的巨大便利,生成了额外的方法的同时,对于构造方法的每个参数都隐式地添加了字段。 不过,样例类最大的好处是它们支持模式匹配
②模式匹配
假定我们想简话前面展示的算术表达式。可用的简化的规则非常多,下只列举一部分:
UnOp("-", UnOp ("-", e)) => e //双重取负
BinOp("+", e, Number(0)) => e //加0
BinOp("*", e, Number(1)) => e // 乘1
用模式匹配定义simplifyTop函数:
def simplifyTop(expr: Expr): Expr = expr match{
case UnOp("-", UnOp ("-", e)) => e
case BinOp("+", e, Number(0)) => e
case BinOp("*", e, Number(1)) => e
case _ => expr
}
执行结果:
3.简单案例
上述编写一个操作算术表达式的类库的案例,对与初学者来说可能存在一定难度,在模式匹配中使用了多种模式,一时难以理解。下面来看一个稍微简单一点的案例。
在本例中,我们有两个继承于常规类Amount类的样例类
abstract class Amount
case class Dollar(vaule: Double) extends Amount //美元类
case class Currency(vaule: Double, unit: String) extends Amount //货币类
我们也可以有针对单例的样例对象:
case object Nothing extends Amount
当我们有一个类型为Amount的对象时,我们可以使用模式匹配来匹配到它的类,并将属性值绑定到变量:
for(amt <- Array(Dollar(10000.0), Currency(10000.0, "RMB"),Nothing)){
val res = amt match{
case Dollar(v) => "美元:$"+v
case Currency(v,u) => "人民币:"+v+" "+u
case _ => "nothing"
}
println(amt +":"+res)
}
执行结果:
三、匹配嵌套结构
样例类经常被用于嵌套结构。例如:某个商品售卖的物品。有时会将物品捆绑在一起打折出售。
abstract class Item
case class Article(description: String, price: Double) extends Item
case class Bundle(description: String, discount: Double, items: Item*) extends Item
因为不用使用new,所以我们可以很容易的给出嵌套对象的定义:
Bundle("Father's day special",20.0,
Article("Scala for the Impatient",39.9),
Bundle("Anchor Distillery Sampler", 11.1,
Article("Old Potrero Straight", 79.8),
Article("Junipero Gin",32.95)
)
)
模式匹配可以匹配到特定的嵌套。 比如:
case Bundle(_, _, Article(dsec,_), _*) => ....
上述代码将desc绑定到Bundle的一个Article的描述。
四、密封类
当你使用样例类来做模式匹配时,你可以想让编译器确保你已经列出了所有可能的选择。你需要将样例类的通用超类声明为sealed。
sealed abstract class Amount
case class Article(description: String, price: Double) extends Item
case class Bundle(description: String, discount: Double, items: Item*) extends Item
密封类的所有子类都必须在与改密封类相同的文件中定义。例如:假设想要添加一个新的美食书类作为另一个样例类:
case class food(description: String, price: Double) extends Item
这个样例类必须在Item所在的文件中定义。