
4.4 样例类
在通常情况下,当你编写一个类的时候,需要实现诸如equals、hashCode、toString、字段访问器(getter/setter)、工厂方法等。这些可能非常耗时且容易出错。Scala提供了“样例类”,可以基于传递给它的主构造方法的值来生成若干方法的实现。可以通过在class关键字之前加上case修饰符来声明样例类,就像这样:

加上case修饰符以后,编译器将会生成若干有用的方法。首先,编译器将会创建一个伴生对象并放入名称为apply的工厂方法中。于是你就可以像这样构造一个新的Person对象:

编译器会把这一行代码重写为对生成的工厂方法apply的调用:Person.apply("Sally", 39)。
其次,编译器会把所有类参数存储为字段,并生成与参数同名的字段访问器(getter/setter)方法。[7]例如,可以像这样访问Person对象的name和age:

再次,编译器会提供toString方法的实现:

此外,编译器会为类生成hashCode方法和equals方法的实现。这些方法会基于传入构造方法的参数来生成结果。举例来说,一个Person对象在进行相等性判断或计算散列值(hash code)时,会同时考虑name和age:

对于那些已经自行实现的方法,编译器不会重新生成或覆盖,而是会沿用你的实现。也可以向类和伴生对象添加其他字段和方法。这里有一个例子,你可以在Person伴生对象中自行定义apply方法,这样编译器就不会生成,而你还可以继续在Person类中添加appendToName方法:

这里的apply方法用于确保名称的首字符是大写的:

也可以调用刚定义的appendToName方法:

最后,编译器还会向类中添加copy方法,并向伴生对象中添加unapply方法。我们将在第13章介绍这些方法的用途。
所有这些约定俗成的做法都为我们带来了很大的便利(代价却不高)。只需要多写一个case修饰符,你的类和对象就会稍微变大。这是因为编译器帮我们生成了额外的方法,以及与每个传入构造方法参数相对应的字段。