對象表達(dá)式與對象聲明
有時候,我們需要創(chuàng)建一個對某個類做了輕微改動的類的對象,而不用為之顯式聲明新的子類。 Kotlin 用對象表達(dá)式和對象聲明處理這種情況。
對象表達(dá)式
要創(chuàng)建一個繼承自某個(或某些)類型的匿名類的對象,我們會這么寫:
window.addMouseListener(object:MouseAdapter() {
overridefunmouseClicked(e:MouseEvent) {/*……*/}
?
overridefunmouseEntered(e:MouseEvent) {/*……*/}
})
如果超類型有一個構(gòu)造函數(shù),則必須傳遞適當(dāng)?shù)臉?gòu)造函數(shù)參數(shù)給它。 多個超類型可以由跟在冒號后面的逗號分隔的列表指定:
openclassA(x:Int) {
publicopenvaly:Int=x
}
?
interfaceB{/*……*/}
?
valab:A=object:A(1),B{
overridevaly=15
}
任何時候,如果我們只需要“一個對象而已”,并不需要特殊超類型,那么我們可以簡單地寫:
funfoo() {
valadHoc=object{
varx:Int=0
vary:Int=0
? ? }
print(adHoc.x+adHoc.y)
}
請注意,匿名對象可以用作只在本地和私有作用域中聲明的類型。如果你使用匿名對象作為公有函數(shù)的返回類型或者用作公有屬性的類型,那么該函數(shù)或?qū)傩缘膶?shí)際類型會是匿名對象聲明的超類型,如果你沒有聲明任何超類型,就會是?Any。在匿名對象中添加的成員將無法訪問。
classC{
// 私有函數(shù),所以其返回類型是匿名對象類型
privatefunfoo()=object{
valx:String="x"
? ? }
?
// 公有函數(shù),所以其返回類型是 Any
funpublicFoo()=object{
valx:String="x"
? ? }
?
funbar() {
valx1=foo().x// 沒問題
valx2=publicFoo().x// 錯誤:未能解析的引用“x”
? ? }
}
對象表達(dá)式中的代碼可以訪問來自包含它的作用域的變量。
funcountClicks(window:JComponent) {
varclickCount=0
varenterCount=0
?
window.addMouseListener(object:MouseAdapter() {
overridefunmouseClicked(e:MouseEvent) {
clickCount++
? ? ? ? }
?
overridefunmouseEntered(e:MouseEvent) {
enterCount++
? ? ? ? }
? ? })
// ……
}
對象聲明
單例模式在一些場景中很有用, 而 Kotlin(繼 Scala 之后)使單例聲明變得很容易:
objectDataProviderManager{
funregisterDataProvider(provider:DataProvider) {
// ……
? ? }
?
valallDataProviders:Collection<DataProvider>
get()=// ……
}
這稱為對象聲明。并且它總是在?object?關(guān)鍵字后跟一個名稱。 就像變量聲明一樣,對象聲明不是一個表達(dá)式,不能用在賦值語句的右邊。
對象聲明的初始化過程是線程安全的并且在首次訪問時進(jìn)行。
如需引用該對象,我們直接使用其名稱即可:
DataProviderManager.registerDataProvider(……)
這些對象可以有超類型:
objectDefaultListener:MouseAdapter() {
overridefunmouseClicked(e:MouseEvent) {……}
?
overridefunmouseEntered(e:MouseEvent) {……}
}
注意:對象聲明不能在局部作用域(即直接嵌套在函數(shù)內(nèi)部),但是它們可以嵌套到其他對象聲明或非內(nèi)部類中。
伴生對象
類內(nèi)部的對象聲明可以用?companion?關(guān)鍵字標(biāo)記:
classMyClass{
companionobjectFactory{
funcreate():MyClass=MyClass()
? ? }
}
該伴生對象的成員可通過只使用類名作為限定符來調(diào)用:
valinstance=MyClass.create()
可以省略伴生對象的名稱,在這種情況下將使用名稱?Companion:
classMyClass{
companionobject{ }
}
?
valx=MyClass.Companion
其自身所用的類的名稱(不是另一個名稱的限定符)可用作對該類的伴生對象 (無論是否具名)的引用:
classMyClass1{
companionobjectNamed{ }
}
?
valx=MyClass1
?
classMyClass2{
companionobject{ }
}
?
valy=MyClass2
請注意,即使伴生對象的成員看起來像其他語言的靜態(tài)成員,在運(yùn)行時他們?nèi)匀皇钦鎸?shí)對象的實(shí)例成員,而且,例如還可以實(shí)現(xiàn)接口:
interfaceFactory<T>{
funcreate():T
}
?
classMyClass{
companionobject:Factory<MyClass>{
overridefuncreate():MyClass=MyClass()
? ? }
}
?
valf:Factory<MyClass>=MyClass
當(dāng)然,在 JVM 平臺,如果使用?@JvmStatic?注解,你可以將伴生對象的成員生成為真正的靜態(tài)方法和字段。更詳細(xì)信息請參見Java 互操作性一節(jié) 。
對象表達(dá)式和對象聲明之間的語義差異
對象表達(dá)式和對象聲明之間有一個重要的語義差別:
對象表達(dá)式是在使用他們的地方立即執(zhí)行(及初始化)的;
對象聲明是在第一次被訪問到時延遲初始化的;
伴生對象的初始化是在相應(yīng)的類被加載(解析)時,與 Java 靜態(tài)初始化器的語義相匹配。