有時(shí)候,我們需要?jiǎng)?chuàng)建一個(gè)類被部分修改的對象局劲,而不需要明確地聲明一個(gè)新的子類飘痛。Java用匿名內(nèi)部類來處理這種情況。Kotlin則用對象表達(dá)式和對象聲明略微概括了這個(gè)概念容握。
對象表達(dá)式(Object expressions)
要?jiǎng)?chuàng)建一個(gè)繼承某種類型(或類型s)的匿名類的對象宣脉,我們可以這樣寫:
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
// ...
}
override fun mouseEntered(e: MouseEvent) {
// ...
}
})
如果基類具有構(gòu)造函數(shù),則必須傳遞適當(dāng)?shù)臉?gòu)造函數(shù)參數(shù)剔氏。 冒號后可以用逗號將基類分隔:
open class A(x: Int) {
public open val y: Int = x
}
interface B {...}
val ab: A = object : A(1), B {
override val y = 15
}
如果我們需要“只是一個(gè)對象”塑猖,沒有任何重要的基類,我們可以簡單地寫:
fun foo() {
val adHoc = object {
var x: Int = 0
var y: Int = 0
}
print(adHoc.x + adHoc.y)
}
注意到谈跛,匿名對象僅可以被用作本地類型和私有聲明羊苟。如果你使用一個(gè)匿名對象作為一個(gè)公有函數(shù)的返回類型,或公有屬性的類型感憾,函數(shù)或?qū)傩缘恼鎸?shí)類型將是匿名對象的基類類型蜡励,如果沒有聲明任何超類型,則匿名對象的類型將是"Any"阻桅。在匿名對象中添加的成員將無法訪問凉倚。
class C {
// Private function, so the return type is the anonymous object type
private fun foo() = object {
val x: String = "x"
}
// Public function, so the return type is Any
fun publicFoo() = object {
val x: String = "x"
}
fun bar() {
val x1 = foo().x // Works
val x2 = publicFoo().x // ERROR: Unresolved reference 'x'
}
}
就像Java中的匿名內(nèi)部類對象的表達(dá)式,對象表達(dá)式的代碼可以訪問表達(dá)式之外的變量嫂沉。(不像Java稽寒,是不受限制的final變量)。
fun countClicks(window: JComponent) {
var clickCount = 0
var enterCount = 0
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
clickCount++
}
override fun mouseEntered(e: MouseEvent) {
enterCount++
}
})
// ...
}
對象聲明(Object declarations)
單例是一個(gè)非常有用的模式趟章。Kotlin讓聲明單例更加的容易:
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
// ...
}
val allDataProviders: Collection<DataProvider>
get() = // ...
}
這被稱作對象聲明杏糙,且其名字始終跟隨object關(guān)鍵字慎王。就像變量聲明一樣,對象聲明不是一個(gè)表達(dá)式宏侍,不能在賦值語句的右側(cè)使用赖淤。
為了引用這個(gè)對象,我們直接使用它的名字:
DataProviderManager.registerDataProvider(...)
如此對象也可以擁有基類:
object DefaultListener : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
// ...
}
override fun mouseEntered(e: MouseEvent) {
// ...
}
}
注意:對象聲明不是本地的(即直接嵌套在函數(shù)內(nèi))谅河,它們可以嵌套到其他對象聲明或非內(nèi)部類中咱旱。
伴生對象(Companion Objects)
在一個(gè)類內(nèi)部的對象聲明可以被companion
關(guān)鍵字標(biāo)記:
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
伴生對象的成員可以通過簡單的類名作為限定符來調(diào)用:
val instance = MyClass.create()
伴生對象的名稱可以省略,此時(shí)該對象的名稱可以使用Companion
代替:
class MyClass {
companion object {
}
}
val x = MyClass.Companion
注意旧蛾,盡管伴生對象的成員看起來像其他語言里的static成員,運(yùn)行時(shí)他們?nèi)匀皇钦鎸?shí)對象的實(shí)例成員蠕嫁,并且它們可以實(shí)現(xiàn)接口:
interface Factory<T> {
fun create(): T
}
class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}
但是锨天,在JVM上,如果使用@JvmStatic
注解剃毒,則可以將伴生對象作為實(shí)際靜態(tài)方法和字段生成病袄。
對象表達(dá)式和聲明的語義區(qū)別(Semantic difference between object expressions and declarations)
對象表達(dá)式和對象聲明之間存在一個(gè)重要的語義差異:
- 對象表達(dá)式在使用它們的地方被立即初始化并執(zhí)行
- 對象聲明則是懶初始化,只有第一次引用它們的時(shí)候
- 當(dāng)對應(yīng)得類被加載(解析)時(shí)赘阀,伴生對象被初始化益缠,和Java中的static初始化一致。