Kotlin中的object和companion object关键字

一. object

1. object 表达式

可以创建匿名内部类

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

通过object 创建的匿名内部类可以继承父类,实现多个接口,如果父类默认构造方法有参数需要传递合适的参数

open class A(x: Int) {
    public open val y: Int = x
}
interface B {...}
 
val ab: A = object : A(1), B {
    override val y = 12
}

有时候需要一个仅仅是一个对象的变量,不继承其他类,也可以直接用object

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

需要注意的是,这种匿名的对象只能被用在局部或者private声明,如果将匿名对象作为public方法的返回值或者作为一个public的属性,那么【这个方法的返回值或属性的类型】的真实值被置为匿名对象的父类类型,如果没有继承就是Any,那么被声明在匿名对象内部的变量便不可访问。

class C {
    //private方法,返回值是匿名对象的类型
    private fun foo() = object {
        val x: Int = 233
    }
    //public方法,返回值是 Any
    fun publicFoo() = object {
        val x: Int = 666
    }
    fun bar() {
        val x1 = foo().x
        val x2 = publicFoo().x  //会发生错误,找不到这个变量
    }
}

同Java一样object声明的匿名内部类可以访问局部变量,但是Java只能访问final修饰的,object则没有这个限制

fun countClicks(window: JCompenet) {
    var clickCount = 0
    var enterCount = 0
 
    window.addMouseListener(object: MouseAdapter) {
        override fun mouseClicked(e: Event) {
            clickCount++
        }
        override fun mouseEntered(e: Event) {
            enterCount++
        }
    }
}

2. object 声明

当用object 声明一个类时,表明这个类是一个单例类,这比在Java中声明一个单例类要简单得多

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

在使用时,可以直接使用类名作为对象来调用方法

DataProviderManaer.registerDataProvider(...)

这种单例类也可以继承其他类

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

注意object声明不能用在方法和inner内部类中,但是能嵌套在其他object声明和嵌套类中。

另外还需要注意:

object 定义后即刻实例化

因此 object 不能有定义构造函数

定义在类内部的 object 并不能访问类的成员

二. companion object

和 object 不同, companion object 的定义完全属于类的本身,所以 companion object 肯定是不能脱离类而定义在全局之中。它类似 Java 里的 static 变量,所以我们定义 companion object 变量的时候也一般会使用大写的命名方式。

和 object 类似,可以给 companion object 命名,也可以不给名字,这个时候它会有个默认的名字: Companion ,而且,它只在类里面能定义一次:

class MyClass {
    companion object CompanionName {
        val INNER_PARAMETER = "can only be inner"
        fun newInstance() = MyClass("name")
    }
}
class MyClass1 {
    companion object {
        val INNER_PARAMETER = "can only be inner"
    }
}
fun main(vararg args:String) {
    println(MyClass.CompanionName.INNER_PARAMETER == MyClass1.INNER_PARAMETER)  //print: true
    println(MyClass1.Companion.INNER_PARAMETER == MyClass1.INNER_PARAMETER)     //print: true
}

扩展功能是Kotlin的一个强大的特性,一个类的companion object也可以进行扩展

class MyClass {
    companion object {
        val INNER_PARAM = "hhhhh"
    }
    fun main(varargs args: String) {
        MyClass.Companion.foo() {
            print(this.INNER_PARAM)
        }
        MyClass.foo()
    }
}

虽然companion object中声明的变量类似于Java中的静态变量,但是在运行时他们仍然是一个真正实例的成员变量,所以companion可以实现一个接口

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

可以使用 @JvmStatic 使Companion object 的成员真正成为静态成员。

ps:

object表达式是在使用的地方立即被初始化和执行的

object声明(一个类)是延迟加载的,只有当第一次被访问时才会初始化,所以被用来实现单例

companion object是当包含它的类被加载时就初始化了的,这一点和Java的static还是一样的

三. 总结

通过上面的一些介绍,对于object 和companion object 的用法有了一定的了解,其实官方并不建议我们到处滥用object 关键字,因为它不利于控制也不易于测试,毕竟它会在声明时就初始化。一般情况下我们使用的使用就是匿名内部类或者单例模式,而companion object一般用来声明静态域。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容