来看Kotlin官方文件,什么是 Object expressions and declarations

Object expressions and declarations

https://kotlinlang.org/docs/object-declarations.html

这边会介绍到什么是

Object expressions
Object declarations

还有什么是

anonymous Object expressions

以及他的衍生,也是大家常用的类似static class宣告方式

Companion Object
anonymous Companion Object


今天在Twitter(x)上看到有一个的发文,他说他在一场会议看到了一个写法

Creating anonymous objects from scratch

val helloWorld = object {    val hello = "Hello"    val world = "World"    // object expressions extend Any, so `override` is required on `toString()`    override fun toString() = "$hello $world"}println(helloWorld.hello)

就可以得到

Hello

会很讶异的是,匿名类别我们知道,但没想到除了宣告匿名类别外,居然可以额外添加成员功能?

其实后来仔细想一想,本来就可以,你一定也使用过匿名类别的产生,差别在于这边会多上一个:去实做这个OnViewClickListener,但是确不知道其实也可以用来产生一个没实作其他类别的物件写法,可以看下面宣告方式,你一定很熟悉

view.setOnClickListener(object: OnViewClickListener() {  ...})

然而你会发现,以前也不知道为什么,就是固定写object :这样方式,来匿名实做介面使用,但其实这是有使用原理的

先看看官方对于Object express的描述

Object expressions create objects of anonymous classes, that is, classes that aren't explicitly declared with the class declaration. Such classes are useful for one-time use. You can define them from scratch, inherit from existing classes, or implement interfaces. Instances of anonymous classes are also called anonymous objects because they are defined by an expression, not a name.

当中提到,类别不是明确的宣告于某一个类别中,这样的类别在一次性的时候是有用的. 你可以定义他在来继承存在的类别或者介面实作。匿名类别的实例被称为匿名物件,因为他们被定义成一种表示,而非一个名称

有这样定义就更明确的,可以往下看

Inheriting anonymous objects from supertypes

其实就是这个章节提到,也称为Inheriting anonymous objects from supertypes ,官方的範例是这样

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 { // <-----同时匿名实作或继承A与B    override val y = 15}

也就是Android开发者常见,平常都在使用的匿名实做OnViewClickListener的写法,因此如果今天我们不进行介面实作,他就会变成anonymous Object expressions,也就是刚刚最上面的写法

val helloWorld = object {    val hello = "Hello"    val world = "World"    // object expressions extend Any, so `override` is required on `toString()`    override fun toString() = "$hello $world"}println(helloWorld.hello)

仔细比对一下,是不是很熟悉,也不会觉得这样写法很特别了,

但你可能会问

Q.如果没有实作介面或者继承,会有什么原因会需要使用这样的写法?

个人也是没用过的(毕竟我也才刚了解),但根据官方文件,如果你是one-time use,或者你只是要在某一个function scope中定义一个有属性有方法的匿名类别,就很适合这样使用,我想到的就是你在一个function中刚好要处理一个複杂的case,也懒得去独立建立一个data class然后还要想一个命名给他的时候,就非常适合用匿名物件(anonymous object),譬如类似这样,但这可能不是一个很好的範例,等我想到再补充,总之你可以在一个function中去再次整理你的程式码,聚合,当然用透过inner function也是可以

fun test(val outterName: String,val outterAge: Int? = null) {    // 在一个function裏面,有多个栏位想要放在一起    // 但又不想额外宣告一个private fun来增加本身类别的function 暴露    val result = object {        val name = outterName        val age = outterAge ?: "Uknow"                fun getDisplay() = "$name -($age)"    }        // 最终直接这样使用    textView.text = result.getDisplay()}

Accessing variables from anonymous objects

另外是如果在同一个scope中,匿名物件是可以直接去存取同一个scope的变数

fun countClicks(window: JComponent) {    var clickCount = 0 // <-----------在fun scope中的变数    var enterCount = 0     window.addMouseListener(object : MouseAdapter() {        override fun mouseClicked(e: MouseEvent) {            clickCount++  // <------------在匿名物件中可以直接读取或者修改是没问题的        }        override fun mouseEntered(e: MouseEvent) {            enterCount++        }    })    // ...}

如同上面,他可以直接存取上面的clickCount的变数


Object declarations 物件宣告

这写法应该是大家都用过的,也就是java的static class要转成kotlin的时候都会透过object declarations来宣告使用,并且具有Singleton的特性,与java static class又不同,他是可以被当成参数传递的

object DataProviderManager {    fun registerDataProvider(provider: DataProvider) {        // ...    }    val allDataProviders: Collection<DataProvider>        get() = // ...}

上面的方法就称为object declaration,中文应该就可以称为物件宣告吧? 这样的方式就不是匿名物件,当然他也可以用来实作或继承其他类别

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

所以其实我觉得官方文件应该是要先介绍这个,然后可以再提到他如果省略前方的object name,就可以变成匿名物件,应该会比较好理解

Data objects 资料物件

另外他也可以像是data class一样变成data objects

data object MyDataObject {    val x: Int = 3}fun main() {    println(MyDataObject) // MyDataObject    println(MyDataObject.x) // 3}

但差别在于,因为物件实例没办法有建构子,所以也不会有所谓的建构子参数,比较适合用来宣告成一个具有不可变的常数物件的感觉


Companion objects

这个应该也是很常见的一个语法,也就是在class中另外宣告一个Companion objects,可以匿名,也可以进行命名,而中文好像还没有官方的正式命名,可以称为伴生、协同、共生于class上的物件名称,也就是他寄身于于本的class name上,譬如下方式MyClass,他就会存在于MyClass中,所以这就看自己怎么翻译理解啰

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

透过这样产生实例

val instance = MyClass.create()

也可以透过Companion取出Companion objects

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

接着你也可以不命名,变成匿名Companion objects

class MyClass1 {    companion object Named { }}val x = MyClass1class MyClass2 {    companion object { }}val y = MyClass2

另外比较少见的,Companion objects也可以实作另一个介面在

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

总整理

以上就是关于官方文件谈到的Object expressions and declarations的章节内容,有很多在书本上没有提到的部分,或者大部分都没看过的额外一些用法,在来整理一下

所以回过头来先知道 Object 使用方式

http://img2.58codes.com/2024/20125654CfTW3Lsnhi.png

不宣告名称他就变成一个匿名物件

http://img2.58codes.com/2024/20125654huMrvUERyI.png

将匿名物件直接产生来使用
val manager = object {    fun registerDataProvider(provider: DataProvider) {        // ...    }    val allDataProviders: Collection<DataProvider>        get() = // ...}

使用

manager.registerDataProvider(..) // 直接呼叫使用

或是

实做另一个介面,有物件名称

http://img2.58codes.com/2024/20125654RnRDo2PCRk.png

实做另一个介面,改成匿名物件

http://img2.58codes.com/2024/201256542MIPHGlySw.png

简化一下,直接放进method中就变成常见的匿名实作介面的写法
view.setMouseAdapter(object: MouseAdapter {    override fun mouseClicked(e: MouseEvent) { /*...*/ }    override fun mouseEntered(e: MouseEvent) { /*...*/ }    fun registerDataProvider(provider: DataProvider) {        // ...    }    val allDataProviders: Collection<DataProvider>        get() = // ...})

这样应该就会更清楚了,可以直接弄懂Object declared后了解什么是Object expression,我个人也蛮喜欢到官方网站去挖宝的,下次有看到其他觉得特别或者少见的语法,在来分享,如果有发现内容有任何错误,也欢迎留言给予建议。


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章