Kotlin 六 對象表達式和對象聲明

一 對象表達式和對象聲明

Kotlin 用對象表達式和對象聲明來實現建立一個對某個類作了輕微改動的類的對象,且不須要去聲明一個新的子類。swift

對象表達式

經過對象表達式實現一個匿名內部類的對象用於方法的參數中:ide

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
}

若是超類型有一個構造函數,則必須傳遞參數給它。多個超類型和接口能夠用逗號分隔。url

經過對象表達式能夠越過類的定義直接獲得一個對象:對象

fun main(args: Array<String>) {
    val site = object {
        var name: String = "菜鳥教程"
        var url: String = "www.runoob.com"
    }
    println(site.name)
    println(site.url)
}

 請注意,匿名對象能夠用做只在本地和私有做用域中聲明的類型。若是你使用匿名對象做爲公有函數的 返回類型或者用做公有屬性的類型,那麼該函數或屬性的實際類型 會是匿名對象聲明的超類型,若是你沒有聲明任何超類型,就會是 Any。在匿名對象 中添加的成員將沒法訪問。blog

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  // 錯誤:未能解析的引用「x」
    }
}

 在對象表達中能夠方便的訪問到做用域中的其餘變量: 繼承

 

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++
        }
    })
    // ……
}

 

對象聲明

Kotlin 使用 object 關鍵字來聲明一個對象。教程

Kotlin 中咱們能夠方便的經過對象聲明來得到一個單例。接口

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

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

引用該對象,咱們直接使用其名稱便可:  作用域

DataProviderManager.registerDataProvider(……)

固然你也能夠定義一個變量來獲取獲取這個對象,當時當你定義兩個不一樣的變量來獲取這個對象時,你會發現你並不能獲得兩個不一樣的變量。也就是說經過這種方式,咱們得到一個單例。  

var data1 = DataProviderManager
var data2 = DataProviderManager
data1.name = "test"
print("data1 name = ${data2.name}")  

 如下實例中,兩個對象都輸出了同一個 url 地址:

object Site {
    var url:String = ""
    val name: String = "菜鳥教程"
}
fun main(args: Array<String>) {
    var s1 =  Site
    var s2 = Site
    s1.url = "www.runoob.com"
    println(s1.url)
    println(s2.url)
}

對象能夠有超類型:  

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

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

與對象表達式不一樣,當對象聲明在另外一個類的內部時,這個對象並不能經過外部類的實例訪問到該對象,而只能經過類名來訪問,一樣該對象也不能直接訪問到外部類的方法和變量。 

class Site {
    var name = "菜鳥教程"
    object DeskTop{
        var url = "www.runoob.com"
        fun showName(){
            print{"desk legs $name"} // 錯誤,不能訪問到外部類的方法和變量
        }
    }
}
fun main(args: Array<String>) {
    var site = Site()
    site.DeskTop.url // 錯誤,不能經過外部類的實例訪問到該對象
    Site.DeskTop.url // 正確
}

 

伴生對象

類內部的對象聲明能夠用 companion 關鍵字標記,這樣它就與外部類關聯在一塊兒,咱們就能夠直接經過外部類訪問到對象的內部元素。 

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

val instance = MyClass.create()   // 訪問到對象的內部元素

咱們能夠省略掉該對象的對象名,而後使用 Companion 替代須要聲明的對象名:  

class MyClass {
    companion object {
    }
}

val x = MyClass.Companion

 

注意:一個類裏面只能聲明一個內部關聯對象,即關鍵字 companion 只能使用一次。

請伴生對象的成員看起來像其餘語言的靜態成員,但在運行時他們仍然是真實對象的實例成員。例如還能夠實現接口:

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


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

 

對象表達式和對象聲明之間的語義差別

對象表達式和對象聲明之間有一個重要的語義差異:

  • 對象表達式是在使用他們的地方當即執行的

  • 對象聲明是在第一次被訪問到時延遲初始化的

  • 伴生對象的初始化是在相應的類被加載(解析)時,與 Java 靜態初始化器的語義相匹配

相關文章
相關標籤/搜索