9.2 独立对象和伴生对象
前面提到的 MarkerFactory
是一个独立对象(stand-alone object
)。它和任何类都没有自动的联系,尽管我们用它来管理 Marker
的实例。
可以选择将一个单例关联到一个类。
这样的单例,其名字和对应类的名字一致,因此被 称为
在前面的例子中,我们可以通过伴生对象来规范 Marker
实例的创建。
类与其伴生对象间没有边界---
一个类的构造器,包括主构造器,也可以标记为 private
。 我们可以结合这两个特性来解决前一节末尾特别提出的问题。
下面是使用一个伴生对象对 Marker 这个例子进行的重写。
import scala.collection._
// 主构造函数私有. 将来只能在伴生对象中访问
class Marker private(val color: String) {
println(s"Creating ${this}")
override def toString = s"marker color $color"
}
object Marker {
private val markers = mutable.Map(
"red" -> new Marker("red"),
"blue" -> new Marker("blue"),
"yellow" -> new Marker("yellow"))
def getMarker(color: String): Marker = markers.getOrElseUpdate(color, new Marker(color))
def main(args: Array[String]): Unit = {
println(Marker getMarker "blue")
println(Marker getMarker "blue")
println(Marker getMarker "red")
println(Marker getMarker "red")
println(Marker getMarker "green")
}
}
说明:
Marker
的构造器被声明为private
;然而,它的伴生对象可以访问它。因此,我们可 以在伴生对象中创建
Marker
的实例。如果试着在类或者伴生对象之外创建
Marker
的实例, 就会收到错误提示。每一个类都可以拥有伴生对象 ,伴生对象和相应的伴生类可以放在同一个文件中。在 Scala 中,伴生对象非常常见,并且通常提供一些类层面的便利方法。伴生对象还能作为一非常好的变通方案,弥补 Scala 中缺少
static
成员的事实
Scala 中的 static
Scala 没有 static
关键字,直接在一个类中允许 static
字段和 static
方法会破坏 Scala 提供的纯面向对象模型。
与此同时,Scala 通过单例对象和伴生对象完整支持类级别的操作和属性。
如果能够从 Marker
中获得所支持的颜色,就非常棒。
但是,直接在这个类的任何实例中查询并没有意义,因为这是一个类级别的操作。
换句话说,如果我们是在用 Java 写代码,就会在类 Marker
中把这个查询方法写成一个 static
方法。
但是,Scala 并不提供 static
。一开始就是这样设计的,以至于这些方法在单例对象和伴生对象中作为常规方法存在。
我们来改一下 Marker
这个例子,并在伴生对象 中创建一些方法。
package scala.bj2
import scala.collection._
// 主构造函数私有. 将来只能在伴生对象中访问
class Marker private(val color: String) {
println(s"Creating ${this}")
override def toString = s"marker color $color"
}
object Marker {
private val markers = mutable.Map(
"red" -> new Marker("red"),
"blue" -> new Marker("blue"),
"yellow" -> new Marker("yellow"))
def apply(color: String): Marker = markers.getOrElseUpdate(color, new Marker(color))
def supportedColors = markers.keys
def main(args: Array[String]): Unit = {
println(Marker.supportedColors)
println(Marker("blue"))
println(Marker("yellow"))
}
}
说明:
println(s"Supported colors are : ${Marker.supportedColors}")
可以直接在外面这样调用刚刚创建的方法, 就像在调用 Java 的静态方法.
apply
方法
如果不用 new
关键字就可以创建伴生类的实例, 是比较舒服的操作。 特殊的 apply()
方法就是达到这种效果的关键。
在前面的例子中, 当我们调用 Marker ("blue")
时,实际上在调用 Marker.apply("blue")
。这是一种创建或者获得实例的轻量级语法。
在字节码层面上,单例中方法会被创建为 static
方法。这从与 Java 的互操作性上讲,是一个好消息。
小结:
Scala 中伴生对象采用
object
关键字声明,伴生对象中声明的全是 "静态"内容,可以通过伴生对象名称直接调用伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致
伴生对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问
从语法角度来讲,所谓的伴生对象其实就是类的静态方法和静态属性的集合
伴生对象的声明应该和伴生类的声明在同一个源码文件中(如果不在同一个文件中会运行错误!),但是如果没有伴生类,也就没有所谓的伴生对象了,所以放在哪里就无所谓了。
类和伴生对象之间可以互相访问对方的私有成员.