对象是只有一个实例的类。当它被引用的时候才会被惰性创建,就像一个惰性的val
。
作为顶层值,对象是单例的。
不论作为封闭类成员还是本地值,它表现得完全像一个惰性的val
。
定义一个单例对象
对象是一个值。对象的定义跟类差不多,只不过使用的关键字是object
:
1 | object Box |
下面是带方法的对象的例子:
1 | package logging |
方法info
可以在程序的任何位置导入使用。创建这样的工具方法是单例对象的常见用法。
1 | import logging.Logger.info |
因为使用了导入语句import logging.Logger.info
,所以info
在这里是可见的。
导入语句对于被导入的对象来说需要一个“可靠的路径”,而对象就是一个“可靠的路径”。
注意:如果一个对象不是处于顶层,而是嵌套在其他的类或者对象当中,那么这个对象则与其他成员一样是“路径依赖”的。也就是说,如果给定两种类型的饮料:class Milk
和class OrangeJuice
,另外有一个类成员object NutritionInfo
,它依赖于相对于它封闭的实例——牛奶或者橙汁,这种情况下milk.NutritionInfo
和oj.NutritionInfo
则是完全不同的东西。
伴生对象
与类同名的对象被称为伴生对象。相对地,这个类也是该对象的伴生类。一个伴生类或者对象可以互相访问同伴的私有成员。虽然伴生对象的方法不属于其伴生类的实例,但是我们可以在这个类中使用它们。
1 | import scala.math._ |
类class Circle
有一个明确声明的成员area
,它属于该类所有的实例,而单例object Circle
有一个方法calculateArea
,它对于所有的实例也都是可用的。
每个伴生对象都可以拥有工厂方法:
1 | class Email(val username: String, val domainName: String) |
对象Email
包含了一个工厂方法fromString
,它接收一个String来创建一个Email
实例。这里返回了Option[Email]
以防转换错误。
注意:如果一个类或者对象具有伴生对象或者伴生类,那么必须要定义在同一个文件当中。如果要在交互式解释器(REPL)当中定义伴生类和伴生对象的话,要么把它们定义在同一行,要么进入:paste
模式。
Java程序员需要注意
Java中的static
成员,在Scala中被设计为一个伴生对象的普通成员。如果在Java代码中使用一个伴生对象,会在其伴生类中使用static
修饰符来定义成员。这被称为_静态转发_。即使你并没有定义一个伴生类,这也会自动发生。