Kotlin 用對(duì)象表達(dá)式和對(duì)象聲明來實(shí)現(xiàn)創(chuàng)建一個(gè) 對(duì)某個(gè)類做了輕微改動(dòng) 的類的對(duì)象,且不需要去聲明一個(gè)新的子類。
對(duì)象表達(dá)式和對(duì)象聲明之間的語義差異
對(duì)象表達(dá)式和對(duì)象聲明之間有一個(gè)重要的語義差別:
- 對(duì)象表達(dá)式是在使用他們的地方立即執(zhí)行的
- 對(duì)象聲明是在第一次被訪問到時(shí)延遲初始化的
- 伴生對(duì)象的初始化是在相應(yīng)的類被加載(解析)時(shí),與 Java 靜態(tài)初始化器的語義相匹配
對(duì)象表達(dá)式
通過對(duì)象表達(dá)式實(shí)現(xiàn)一個(gè)匿名內(nèi)部類的對(duì)象用于方法的參數(shù)中,其實(shí)就是類似java中的new接口{}。
btnOperator.setOnClickListener(object : View.OnClickListener{
override fun onClick(v: View?) {
}
})
簡(jiǎn)化后
btnOperator.setOnClickListener { }
對(duì)象可以繼承于某個(gè)基類,或者實(shí)現(xiàn)其他接口:
如果超類型有一個(gè)構(gòu)造函數(shù),則必須傳遞參數(shù)給它。多個(gè)超類型和接口可以用逗號(hào)分隔。
open class A(x: Int) {
open val y: Int = x
}
interface B
val cc: A = object : A(1),B {
override val y = 15
}
通過對(duì)象表達(dá)式可以越過類的定義直接得到一個(gè)對(duì)象:
val site = object {
var name: String = "zhongjh"
var gender: String = "男"
}
println(site.name)
println(site.gender)
私有函數(shù)和公有函數(shù)創(chuàng)建對(duì)象
請(qǐng)注意,匿名對(duì)象可以用作只在本地和私有作用域中聲明的類型。如果你沒有聲明任何超類型,就會(huì)是 Any。在匿名對(duì)象 中添加的成員將無法訪問。
// 私有函數(shù),所以其返回類型是匿名對(duì)象類型
private fun foo() = object {
val x: String = "x"
val y: String = "y"
}
// 這個(gè)跟上一個(gè)是差不多一樣的哦
private val foo = object {
val x: String = "x"
val y: String = "y"
}
// 公有函數(shù),所以其返回類型是 Any
fun publicFoo() = object {
val x: String = "x"
}
val x1 = foo().x // 沒問題
val y1 = foo().y // 沒問題
val x2 = publicFoo() // val x2 = publicFoo().x 錯(cuò)誤:未能解析的引用“x”
val x3 = foo.x
在對(duì)象表達(dá)中可以方便的訪問到作用域中的其他變量:
var clickCount = 0
btnOperator.setOnClickListener {
clickCount++;
}
對(duì)象聲明
Kotlin 使用 object 關(guān)鍵字來聲明一個(gè)對(duì)象。
Kotlin 中我們可以方便的通過對(duì)象聲明來獲得一個(gè)單例。是的,都是同一個(gè)數(shù)據(jù),讓我們看看下面兩個(gè)例子
object DataProviderManager {
const val name = "name"
fun registerDataProvider() {
}
}
DataProviderManager.registerDataProvider()
DataProviderManager.name
object Site {
var url:String = ""
val name: String = "zhongjh"
}
val s1 = Site
val s2 = Site
s1.url = "www.zhongjh.com"
tvContent.append("${s1.url}\n")
tvContent.append("${s2.url}\n")
所以他們分別輸出的結(jié)果都是一樣:
對(duì)象聲明有以下兩點(diǎn)不能訪問:
- 內(nèi)部對(duì)象不能直接訪問到外部類的方法和變量。
- 對(duì)象聲明后不能通過外部類的實(shí)例訪問到該對(duì)象,而只能通過類名來訪問。
class ZhongJH {
var name = "zhongjh"
object DeskTop{
var url = "www.zhongjh.com"
fun showName(){
// print{"desk legs $name"} // 錯(cuò)誤,不能訪問到外部類的方法和變量
}
}
}
var zhongjh = ZhongJH()
// zhongjh.DeskTop.url // 錯(cuò)誤,不能通過外部類的實(shí)例訪問到該對(duì)象
ZhongJH.DeskTop.url // 正確
伴生對(duì)象
類內(nèi)部的對(duì)象聲明可以用 companion 關(guān)鍵字標(biāo)記,這樣它就與外部類關(guān)聯(lián)在一起,我們就可以直接通過外部類訪問到對(duì)象的內(nèi)部元素。
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
// 訪問到對(duì)象的內(nèi)部元素
val instance = MyClass.create()
我們可以省略掉該對(duì)象的對(duì)象名,然后使用 Companion 替代需要聲明的對(duì)象名:
class MyClass {
companion object {
}
}
val x = MyClass.Companion
注意:一個(gè)類里面只能聲明一個(gè)內(nèi)部關(guān)聯(lián)對(duì)象,即關(guān)鍵字 companion 只能使用一次。
請(qǐng)伴生對(duì)象的成員看起來像其他語言的靜態(tài)成員,但在運(yùn)行時(shí)他們?nèi)匀皇钦鎸?shí)對(duì)象的實(shí)例成員。例如還可以實(shí)現(xiàn)接口:
interface Factory<T> {
fun create(): T
}
class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}
對(duì)象表達(dá)式和對(duì)象聲明之間的語義差異
對(duì)象表達(dá)式和對(duì)象聲明之間有一個(gè)重要的語義差別:
- 對(duì)象表達(dá)式是在使用他們的地方立即執(zhí)行的
- 對(duì)象聲明是在第一次被訪問到時(shí)延遲初始化的
- 伴生對(duì)象的初始化是在相應(yīng)的類被加載(解析)時(shí),與 Java 靜態(tài)初始化器的語義相匹配