Case Classes 是 Scala 中最有用的编译器技巧之一。
它们本身不是很复杂,能帮助实现非常乏味和无聊的 equals
、hashCode
和 toString
等方法,
还可以配合模式匹配调用 apply
/unapply
方法等等。
在 Scala 中可以像定义普通类一样定义一个 case class,但是需要加上 case
关键字:
case class Circle(radius: Double)
只需要这一行代码,我们就实现了 Value Object 模式。 定义了这样一个 case class 意味着我们自动得到了这些好处:
-
case class 的实例是不可变的(immutable)
-
可以使用
equals
进行比较,并且相等性是基于字段的(*而不是*像普通类那样比较对象相等性) -
它的
hashcode
遵循equals
合同,也是基于这个类的字段 -
它的构造方法参数自动成为
public val
而无需声明(例如上面例子中的radius
) -
它的
toString
是由类名和它所包含的字段的值组成的(对于我们的 Circle,toString 实现为def toString = s"Circle($radius)"
)。
是时候总结一下我们学到的,然后应用到"现实生活"的例子中, 这一次我们要实现一个 Point 类,因为我们需要多于一个字段才能展示 case class 为我们提供的一些有趣特性:
case class Point(x: Int, y: Int) (1)
val a = Point(0, 0) (2)
// a.toString == "Point(0,0)" (3)
val b = a.copy(y = 10) (4)
// b.toString == "Point(0,10)"
a == Point(0, 0) (5)
-
x
和y
被自动地定义成val
成员 -
同时会生成一个
Point
伴生对象,带有apply(x: Int, y: Int)
方法,可以用来创建一个Point
实例 -
自动生成的
toString
方法包含类名和参数值 -
copy(…)
方法允许通过仅改变选择的字段轻松创建派生对象 -
case class 的相等性是基于值的(equals 和 hashCode 是根据 case class 的参数生成的)
不仅如此,case classes 还可以用于_模式匹配_,不论是"普通"还是"提取器(extractor)"语法(赋值给:)
Circle(2.5) match {
case Circle(r) => println("Radius = " + r)
}
val Circle(r) = Circle(4.0)
val r2 = r * r // r2 现在是 16.0