Kotlin 用對象表達式和對象聲明來實現創建一個 對某個類做了輕微改動 的類的對象,且不需要去聲明一個新的子類。
對象表達式和對象聲明之間的語義差異
對象表達式和對象聲明之間有一個重要的語義差別:
- 對象表達式是在使用他們的地方立即執行的
- 對象聲明是在第一次被訪問到時延遲初始化的
- 伴生對象的初始化是在相應的類被加載(解析)時,與 Java 靜態初始化器的語義相匹配
對象表達式
通過對象表達式實現一個匿名內部類的對象用於方法的參數中,其實就是類似java中的new接口{}。
btnOperator.setOnClickListener(object : View.OnClickListener{
override fun onClick(v: View?) {
}
})
簡化後
btnOperator.setOnClickListener { }
對象可以繼承於某個基類,或者實現其他接口:
如果超類型有一個構造函數,則必須傳遞參數給它。多個超類型和接口可以用逗號分隔。
open class A(x: Int) {
open val y: Int = x
}
interface B
val cc: A = object : A(1),B {
override val y = 15
}
通過對象表達式可以越過類的定義直接得到一個對象:
val site = object {
var name: String = "zhongjh"
var gender: String = "男"
}
println(site.name)
println(site.gender)
私有函數和公有函數創建對象
請注意,匿名對象可以用作只在本地和私有作用域中聲明的類型。如果你沒有聲明任何超類型,就會是 Any。在匿名對象 中添加的成員將無法訪問。
// 私有函數,所以其返回類型是匿名對象類型
private fun foo() = object {
val x: String = "x"
val y: String = "y"
}
// 這個跟上一個是差不多一樣的哦
private val foo = object {
val x: String = "x"
val y: String = "y"
}
// 公有函數,所以其返回類型是 Any
fun publicFoo() = object {
val x: String = "x"
}
val x1 = foo().x // 沒問題
val y1 = foo().y // 沒問題
val x2 = publicFoo() // val x2 = publicFoo().x 錯誤:未能解析的引用“x”
val x3 = foo.x
在對象表達中可以方便的訪問到作用域中的其他變量:
var clickCount = 0
btnOperator.setOnClickListener {
clickCount++;
}
對象聲明
Kotlin 使用 object 關鍵字來聲明一個對象。
Kotlin 中我們可以方便的通過對象聲明來獲得一個單例。是的,都是同一個數據,讓我們看看下面兩個例子
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")
所以他們分別輸出的結果都是一樣:
對象聲明有以下兩點不能訪問:
- 內部對象不能直接訪問到外部類的方法和變量。
- 對象聲明後不能通過外部類的實例訪問到該對象,而只能通過類名來訪問。
class ZhongJH {
var name = "zhongjh"
object DeskTop{
var url = "www.zhongjh.com"
fun showName(){
// print{"desk legs $name"} // 錯誤,不能訪問到外部類的方法和變量
}
}
}
var zhongjh = ZhongJH()
// zhongjh.DeskTop.url // 錯誤,不能通過外部類的實例訪問到該對象
ZhongJH.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 靜態初始化器的語義相匹配