美文网首页
Kotlin中的object和companion object关

Kotlin中的object和companion object关

作者: wind_sky | 来源:发表于2019-07-26 14:30 被阅读0次

    一. 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一般用来声明静态域。

    相关文章

      网友评论

          本文标题:Kotlin中的object和companion object关

          本文链接:https://www.haomeiwen.com/subject/ojarrctx.html