Scala比Java更面向对象的一个方面是Scala没有静态成员。替代品是,Scala有单例对象:singleton object。
当单例对象与某个类共享同一个名称时,他被称作是这个类的伴生对象:companion object。你必须在同一个源文件里定义类和它的伴生对象。类被称为是这个单例对象的伴生类:companion class。类和它的伴生对象可以互相访问其私有成员。
定义单例对象不是定义类型(在Scala的抽象层次上说)
类和单例对象间的一个差别是,单例对象不带参数,而类可以。因为你不能用new关键字实例化一个单例对象,你没机会传递给它参数。每个单例对象都被作为由一个静态变量指向的虚构类:synthetic class的一个实例来实现,因此它们与Java静态类有着相同的初始化语法。Scala程序特别要指出的是,单例对象会在第一次被访问的时候初始化。
不与伴生类共享名称的单例对象被称为孤立对象:standalone object。最常见的就是程序入口:
object AbstractTypeTest1 extendsApplication {def newIntSeqBuf(elem1: Int, elem2: Int): IntSeqBuffer =
newIntSeqBuffer {
type T = List[U]
val element = List(elem1, elem2)
}
val buf = newIntSeqBuf(7,8)
println("length = " + buf.length)
println("content = " + buf.element)
}
一个伴生对象的示例:
importscala.collection.mutable.MapclassChecksumAccumulator {
privatevar sum = 0
def add(b: Byte) {
sum += b
}
def checksum(): Int = ~(sum & 0xFF) + 1
}
object ChecksumAccumulator {
privateval cache = Map[String, Int]()
def calculate(s: String): Int =
if(cache.contains(s))
cache(s)
else{
val acc = newChecksumAccumulator
for(c <- s) acc.add(c.toByte)
val cs = acc.checksum()
cache += (s -> cs)
cs
}
}
object Summer {
def main(args: Array[String]) {
println(ChecksumAccumulator.calculate("Every value is an object."))
}
}
输出为:
-248
但是伴生对象如何体现单例的呢?
单例模式就控制类实例的个数f会。一个简单示例:
classWorkerprivate{def work() = println("I am the only worker!")
}
object Worker{
val worker = newWorker
def GetWorkInstance() : Worker = {
worker.work()
worker
}
}
object Job{
def main(args: Array[String]) {
for(i <- 1to5) {
Worker.GetWorkInstance();
}
}
}
class Worker private声明了Worker的首构造函数是私有的,这样Worker的所有构造函数都不能直接被外部调用,因为所有从构造函数都会首先调用其他构造函数(可以是主构造函数,也可以是从构造函数),结果就是主构造函数是类的唯一入口点。
另一方面,Worker.GetWorkInstance();有点类似静态函数调用,但在Scala中这是不对的。Scala会隐式地调用apply来创建一个伴生对象的实例。Scala是一个纯粹的面向对象语言,不允许有任何破坏对象模型的机制存在,比如类的静态变量、函数等。