不屬于類單個實例的方法和值屬于單例對象,用關鍵字object
而不用class
來標記。
package test
object Blah {
def sum(l: List[Int]): Int = l.sum
}
這個sum
方法是全局可用的,并且可以用test.Blah.sum
來引用或引入。
單例對象某種程度來說是定義一個單一使用類,不能直接實例化,并且在object
定義處有一個同名的val
成員。實際上,像val
,單例對象可以被定義為一個特征或類的成員,雖然這不是典型用法。
單例對象可以擴展類和特征。實際上,不帶type parameters的Case類會默認創建一個同名的單例對象,實現了特征[Function*]。
伴生
大部分單例對象不是獨立的,而是和相同名字的類關聯。前面提到的Case類的同名單例對象,就是一個例子。這種情況下,單例對象被稱為類的伴生對象(companion object)
,類稱為對象的伴生類(companion class)
。
Scaladoc對類和其伴生對象之間跳轉做了特殊支持:可以點擊大的"C"或者"O"圓圈圖標跳轉到伴生對象。
class IntPair(val x: Int, val y: Int)
object IntPair {
import math.Ordering
implicit def ipord: Ordering[IntPair] =
Ordering.by(ip => (ip.x, ip.y))
}
看到typeclass示例作為implicit values是很常見的,如上面的在伴生中定義的ipord
。這是因為伴生成員包含在相關值的默認隱式搜索中。
給Java程序員提示
Scala中static
不是一個關鍵字。相反,所有成員都是靜態的,包括類,都應該歸為單例對象。它們可以用相同的語法引用,單獨或者作為一個整體被引入等等。
Java程序員會頻繁定義靜態成員,也許是private
的,作為實例成員的輔助實現。這些都轉為使用伴生對象,一種常用模式是在類中引入伴生對象的成員。如下:
class X {
import X._
def blah = foo
}
object X {
private def foo = 42
}
這種使用方式展現了另外一個特性:在private
上下文中,類和它的伴生對象是友元。object X
可以訪問class X
的私有成員,反之亦然。想要有真正的私有成員,使用private[this]
。
為了給Java提供方便,在單例對象中直接定義的方法(包括var
和val
)也會在伴生類中定義靜態方法,叫做static forwarder
。對于object X
,其它成員可以用X$.MODULE$
靜態字段訪問。如果你把所有實現都放在伴生對象中并且發現就剩下一個不用實例化的類了,那就把類刪掉。static forwarder
仍然會被創建。