95

kotlin 类中的对象和伴生对象有什么区别?

例子:

class MyClass {

    object Holder {
        //something
    }

    companion object {
        //something
    }
}

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

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

它的“静态”(我来自java端)生命周期是否可能存在差异?

4

6 回答 6

75

有两种不同类型的object使用,表达式声明

对象表达式

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

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

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

对象声明

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

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 文档

于 2017-09-11T13:32:38.753 回答
54

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

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

于 2017-05-06T07:20:50.730 回答
15

正如Kotlin in Action所述

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

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

例如,如果您有以下课程

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

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

然后你可以访问这两个对象如下 From 包含类

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))
于 2019-12-13T18:29:44.210 回答
10

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

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

于 2017-11-20T10:06:04.250 回答
6

Companion 对象在加载类时初始化(通常是第一次被正在执行的其他代码引用),而Object声明在第一次访问时被延迟初始化。

请参考https://kotlinlang.org/docs/reference/object-declarations.html底部清楚地定义了这两者之间的区别。

于 2019-07-10T06:03:03.247 回答
3

伴随对象的存在是因为您可以调用伴随对象的函数/属性,就像它是 java 静态方法/字段一样。以及为什么Holder允许你,好吧,没有理由声明嵌套对象是非法的。有时它可能会派上用场。

于 2017-05-06T03:19:42.327 回答