Kotlin學習之-5.11 對象表達式和聲明

Kotlin學習之-5.11 對象表達式和聲明

有時候我們需要創建一個類的實例,並且擴展或者修改其中的一兩個方法,但不想顯式地聲明一個子類。Java中是使用內部類來處理這種情況,Kotlin則使用對象表達式對象聲明擴展了這種概念。

對象表達式

創建一個內部類的對象,並且繼承某些類型,我們這麼寫:

window.addMouseListener(object: MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) {
    }

    override fun mouseEntered(e: MouseEvent) {
    }
})

如果父類型有構造器,必須傳遞合適的構造器參數。多個父類型可以使用逗號分隔的列表來描述,在冒號之後:

open class A(x: Int) {
    public open val y: Int = x
}

interface B { ... }
val ab: A = object: A(1), B {
    override val y = 15
}

有時候我們只需要一個對象,沒有任何父類,我們可以這麼寫:

fun foo() {
    val adHod = object {
        var x: Int = 0
        var y: Int = 0
    }
    print(adHod.x + adHoc.y)    
}   

注意匿名對象僅可以在局部和私有聲明中被用來當做類型。如果使用一個匿名對象作爲一個公用方法的返回值或者公用屬性的類型,那麼方法和屬性的真正類型是聲明成父類的匿名對象,或者Any如果沒有聲明任何父類。給匿名對象添加的成員無法被訪問。

class C {
    // 私有方法,所以返回的類型是匿名對象類型
    private fun foo() = object {
        val x: String = "x"
    }

    // 共有方法,所以返回類型是Any
    fun publicFoo() = object {
        val x: String = "x"
    }   

    fun bar() {
        val x1 = foo().x // 可用
        val x2 = publicFoo().x // 不可用,Unresolved reference 'x'
    }
}

和Java中的匿名內部類一樣,在對象表達式中的代碼可以訪問封裝它的範圍的變量。

fun countClicks(window: JComponet) {
    var clickCount = 0
    var enterCound = 0

    window.addMouseListener(object: MouseAdapter() {
        overfide fun mouseClicked(e: MouseEvent) {
            clickCount++
        }

        overfide fun mouseEntered(e: MouseEvent) {
            clickCount++
        }
    })
}

對象聲明

單例是一種很有用的設計模式,在Kotlin中讓定義單例更加容易:

object DataProviderManager {
    fun registerDataProvider(provider: DataProvider) {
    }

    val allDataProviders: Collection<DataProvider>
        get() = // ...
}

這種方式叫作對象聲明,並且它總有一個object關鍵字。就想變量聲明,一個對象聲明不是一個表達式,不能用作賦值語句的右值。

要引用這個對象,直接使用對象的名字即可:

DataProviderManager.registerDataProvider( ... )

對象還可以有父類:

object DefaultListener : MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) {
    }

    override fun mouseEntered(e: MouseEvent) {
    }
}

注意:對象聲明不能是局部的(例如,不能直接嵌套在函數裏),但是可以嵌套在其他對象聲明或者非內部類中。

夥伴對象

在一個類中的對象聲明可以使用夥伴companion關鍵字。

class MyClass {
    companion object Factory {
        fun create(): MyClass = MyClass()
    }
}

夥伴對象的成員可以簡單地使用類名作爲描述符來調用:

val instance = MyClass.create()

夥伴對象的名字可以省略,這時使用Companion關鍵字來訪問:

class MyClass {
    companion object {
    }
}
val x = MyClass.Companion

注意,景觀夥伴對象的成員和其他語言中的靜態成員很像,但是在運行時他們仍然是實例成員的真正對象,例如,可以實現接口:

interface Factory<T> {
    fun create(): T
}

class MyClass {
    companion object : Factory<MyClass> {
        override fun create(): MyClass = MyClass()
    }
}

但是,在JVM上如果是用@JvmStatic註解,你仍然可以讓夥伴對象的成員生成真正的靜態方法和成員。 詳見Java interoperability

對象表達式和聲明的語法區別

在對象表達式和對象聲明之間有一個重要的語法區別:

  • 對象表達式在使用的時候,是立即執行的或者立即初始化的
  • 對象聲明是延遲初始化地,是在第一次訪問對象的時候。
  • 一個夥伴對象是在對應的類加載(或者解析)的時候初始化的,和Java中的靜態初始化器對應。

PS,我會堅持把這個系列寫完,有問題可以留言交流,也關注專欄Kotlin for Android Kotlin安卓開發

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章