在 kotlin 中命名伴生对象有啥意义

Posted

技术标签:

【中文标题】在 kotlin 中命名伴生对象有啥意义【英文标题】:What is the point of naming a companion object in kotlin在 kotlin 中命名伴生对象有什么意义 【发布时间】:2018-02-01 20:30:49 【问题描述】:

companion objects 的文档有以下示例

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

这里Factory 是伴生对象的名称。然后它继续说:

伴生对象的名称可以省略,在这种情况下将使用名称Companion

但是,我没有看到使用伴随对象名称的示例。

因为每个类只能有一个伴生对象(否则会出现Only one companion object is allowed per class 错误),所以这个名字对我来说就像是一些非常无用的语法糖。

伴生对象的名称实际上可以用来做什么? 为什么要费心为它使用任何名称?

【问题讨论】:

检查这个:***.com/questions/38381748/… @Maddy 似乎解决了为什么将伴生对象称为伴生对象,而不是为什么您可能会费心为伴生对象指定名称。 【参考方案1】:

您可以使用同伴的名称,例如:

MyClass.create()           // not via companion name
MyClass.Companion.create() // via default companion name
MyClass.Factory.create()   // via companion name

名称对于 Kotlin 可能并不重要,因为您可以在不知道存在伴随对象的情况下访问该方法(上面的第一行)。如果您想让对此类功能的访问更加明确,它更像是一种个人风格。

但对于 java interop 则有所不同,因为您必须通过伴随名称访问该函数:

    MyClass.Factory.create();   // with named companion
    MyClass.Companion.create(); // with unnamed comanion

【讨论】:

所以它稍微改变了你调用 java 函数的方式——但是有一些现有的方法可以改变一个类在暴露给 JVM 时的命名方式,特别是@JvmName——为什么不使用这些呢?跨度> 你可以使用@JvmName' for functions, but not for the name of the companion-object`本身。 @MichaelAnderson 还有,@JvmName 仅适用于 JVM,但 Kotlin 还针对 javascript 和本机 AFAIK,当导入伴生对象的特征,或将其称为方法字面量 (callable reference) 时,您需要指定伴生名称。【参考方案2】:

好吧,Kotlin 中的伴生对象不仅仅是语法糖。它们实际上是一种类型。他们能够做更多的事情,而不需要仅仅被视为静态的替代品。

您实际上可以扩展类或实现接口。请参阅下面的示例。

open class Super 
    open fun sayHello() 
        println("Hello")
    


class Some 
    companion object Child : Super() 
        override fun sayHello() 
            super.sayHello()
            println("Hello from companion object")
        
    


fun main() 
    Some.Child.sayHello()

【讨论】:

【参考方案3】:

如果您不使用显式名称,则同伴名称为Companion,因此可以省略,就像您已经引用的那样。

有时您可能希望在调用中使用明确的名称,在您的示例中为 MyClass.Factory.create()。可能出于命名空间的原因。

我也没有看到很多命名伴侣对象的理由。除非您关心 Java 与 Kotlin 代码的互操作。然后,你需要明确地写出同伴的名字。

您可能关心名称的另一个原因是,当您在其上定义扩展函数时:

  fun MyClass.Companion.ext() = "myext"

在这种情况下,如果它有一个像Factory这样的名称,它会更清楚,具体的工厂方法是通过扩展添加的。

【讨论】:

如果您可以拥有多个伴生对象,我认为这很有用 - 这样您就可以将工厂函数分组为一个,实用程序函数分组为另一个。你能做到吗?否则,感觉相当没有意义。 回答我自己的问题 - 每个班级只能有一个伴生对象。否则你会得到一个Only one companion object is allowed per class 错误。 我看到的原因:Java互操作和扩展函数定义 是的。为伴生对象命名的主要目的是定义可以在类本身而不是类的实例上调用的扩展函数。 在使用未命名版本时,您仍然可以在伴随对象上使用扩展功能。 Companion 的默认名称在那里可以正常工作。因此,IMO 仍然不是用户能够命名它的真正理由。【参考方案4】:

但是,我没有看到使用伴随对象名称的示例。

class Person(val name: String)  companion object Loader 
fun fromJSON(jsonText: String): Person = ... 

>>> person = Person.Loader.fromJSON("name: 'Dmitry'") >>> person.name
Dmitry
>>> person2 = Person.fromJSON("name: 'Brent'") >>> person2.name
Brent

【讨论】:

以上是关于在 kotlin 中命名伴生对象有啥意义的主要内容,如果未能解决你的问题,请参考以下文章

深入kotlin- 伴生对象和扩展

深入kotlin- 伴生对象和扩展

深入kotlin- 伴生对象和扩展

聊聊 Scala 的伴生对象及其意义

R8 去除反射所需的 Kotlin 伴生对象

Kotlin基础 4.companion object(伴生对象),更新中