Kotlin:类中的对象和伴侣对象之间的区别


77

Kotlin中的类中的对象和同伴对象有什么区别?

例:

class MyClass {

    object Holder {
        //something
    }

    companion object {
        //something
    }
}

我已经读过,如果包含的参数/方法与其类紧密相关,则应使用该伴随对象。

但是,为什么还可以在类中声明一个普通对象呢?因为它的行为完全类似于同伴,但是必须有一个名称。

它的“静态”(我来自Java方面)生命周期是否可能有所不同?


3
object适用于Singleton和companion object静态方法。Kotlin-对象声明提供了很好的用法说明。
ArtiomLK

Answers:


49

对象可以实现接口。在类内部,定义一个不实现任何接口的简单对象在大多数情况下没有好处。但是,定义实现各种接口(例如Comparator)的多个对象可能非常有用。

在生命周期方面,伴随对象和在类中声明的命名对象之间没有区别。


完善!非常感谢您的解释!
Poweranimal

AFAIK的初始化顺序有所不同
Ilya

有什么区别?我猜想同伴首先被初始化,因为它被绑定到其类上,然后该对象被调用了?
Poweranimal

31
伴随对象是从包含类的静态构造函数初始化的,而普通对象是在第一次访问该对象时被延迟初始化的。
伊利亚

您回答了我想要的...“生命周期”。谢谢。
利卡特·朱利叶斯

60

有两种不同的object用法,即expressionclarification

对象表达

当类需要稍作修改时可以使用对象表达式,但是不必为其创建全新的子类。匿名内部类就是一个很好的例子。

    button.setOnClickListener(object: View.OnClickListener() {
        override fun onClick(view: View) {
            // click event
        }
    })

需要注意的一件事是,匿名内部类可以从封闭范围访问变量,而这些变量不必是final。这意味着在匿名内部类中使用的未考虑的final变量在被访问之前可能会意外更改值。

对象声明

对象声明类似于变量声明,因此不能在赋值语句的右侧使用。对象声明对于实现Singleton模式非常有用。

    object MySingletonObject {
        fun getInstance(): MySingletonObject {
            // return single instance of object
        }
    }

getInstance方法然后可以调用这个样子。

    MySingletonObject.getInstance()

伴侣对象

伴随对象是对象声明的一种特定类型,它允许对象执行与其他语言(例如Java)中的静态对象相似的操作。companion即使在Kotlin中不存在实际的静态概念,添加到对象声明也可以为对象添加“静态”功能。这是带有实例方法和伴随方法的类的示例。

 class MyClass {
        companion object MyCompanionObject {
            fun actsAsStatic() {
                // do stuff
            }
        }

       fun instanceMethod() {
            // do stuff
        }
    }

调用实例方法将如下所示。

    var myClass = MyClass()
    myClass.instanceMethod()

调用伴随对象方法看起来像这样。

    MyClass.actsAsStatic()

有关更多信息,请参阅Kotlin文档


2
谢谢迈克!这应该是答案。
里奇

2
我必须在同伴对象内部使用该方法,MyClass.MyCompanionObject.actsAsStatic()或者MyClass.Companion.actsAsStatic()如果同伴对象没有名称。这是一个新的变化,还是我做错了什么?谢谢。
Supriya

7

首次访问时,对象或对象声明会被延迟初始化。

加载相应的类时,初始化伴随对象。尽管Kotlin并不固有地支持静态成员,但它带来了“静态”本质。


6

正如科特林在行动中所说

在很多情况下,object关键字出现在Kotlin中,但是它们都具有相同的核心思想:该关键字定义了一个类并同时创建了该类的实例(换句话说,是一个对象)。

当涉及一个普通对象和一个伴随对象时,唯一的显着区别是可以通过使用包含类的名称直接访问伴随对象的属性和功能,这使得它看起来像java静态成员访问。

例如,如果您有以下课程

class Temp{
    object Holder{
        fun foo() = 1
    }

    companion object{
        fun foo() = "Hello World"
    }
}

那么您可以按照下面的步骤从包含类访问这两个对象

foo()   // call to companion object function
Holder.foo() // call to plain object function

并从课堂之外

Temp.foo() // call to companion object function
Temp.Holder.foo() // call to plain object function

在幕后,每个对象声明都会创建一个单例。如果是伴随对象,则在包含类的静态初始化器中创建单例对象。但是如果是普通对象,则在首次访问该对象类时会延迟创建单例实例。

您可以通过编译kotlin类然后使用一些Java反编译器反编译生成的类文件来亲自查看。

至于为什么还可以在该类中声明一个普通对象,请考虑以下类,其中成员对象非常有用。

data class Employee(val name: String) {
    object NameComparator : Comparator<Employee> {
         override fun compare(p1: Employee, p2: Employee): Int =
             p1.name.compareTo(p2.name)
    }
}

现在我们可以将员工列表排序为

list.sortedWith(Employee.NameComparator))

3

伴随对象之所以存在,是因为您可以像在Java静态方法/字段中那样调用伴随对象的函数/属性。出于为什么Holder允许您访问的原因,没有理由声明嵌套对象是非法的。有时可能会派上用场。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.