Kotlin中的Companion object:替代Java static及性能分析

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

为什么Kotlin没有static,却有companion object?深入解析性能与逻辑



如果您从Java转向Kotlin,那么您在StackOverflow上遇到的第一个问题(这个问题确实获得了数千次浏览)会是:“为什么我们要用companion object代替Java static?这会不会拖慢应用程序?”



老实说:当我第一次看到companion object这个结构时,我觉得Kotlin团队只是想把事情搞复杂。为什么要写更多的代码,而不是直接复制Java中的static?但深入研究后,我明白了:这不是一时兴起,而是一个深思熟虑的架构决策,它让代码更清晰、更安全……而且,是的,有时甚至更快。



在本文中,我们不仅仅是翻译StackOverflow上的答案。我们将深入解析companion object在底层是如何工作的,将其性能与Java static进行比较,并展示何时使用它,以及何时选择其他方式。



用简单的话说,什么是companion object?



在Java中,静态字段和方法属于类,而不是实例。Kotlin决定放弃static关键字,因为它违反了面向对象编程原则(静态不能被重写,它不是对象的一部分)。相反,Kotlin提供了伴生对象。



假设您有一个User类。您想存储一个常量MAX_AGE和一个工厂方法createGuest()。在Java中,您会这样写:



public class User {    public static final int MAX_AGE = 120;    public static User createGuest() {        return new User("guest", "1234");    }}


在Kotlin中,它看起来像这样:



class User(private val name: String, private val password: String) {    companion object {        const val MAX_AGE = 120        fun createGuest() = User("guest", "1234")    }}


请注意:companion object不仅仅是“static块”。它是一个完整的对象,存在于您的类内部。它可以有接口、扩展,甚至有自己的名称(如果您指定的话)。



Companion object在底层是如何工作的?(从JVM视角看)



为了理解性能,让我们看看字节码。当您编写companion object时,Kotlin编译器会执行以下操作:



  • 创建一个名为Companion(或您自定义的名称)的内部静态类。
  • 将companion object中的所有字段和方法放入这个类中。
  • 如果您使用@JvmStatic注解,编译器还会在原始类中额外生成静态包装方法。


使用@JvmStatic注解的示例:



class User {    companion object {        @JvmStatic        fun createGuest() = User()    }}


现在,从Java中您可以像调用普通静态方法一样调用User.createGuest()。如果没有这个注解,您必须写成User.Companion.createGuest()。



性能结论:如果您只使用Kotlin,则没有额外的开销。如果从Java调用,则多了一层间接性(通过Companion对象)。但JVM的JIT编译器能很好地内联这些调用,因此速度差异趋近于零。



性能:companion object vs Java static



让我们破除一个主要迷思:companion object不会拖慢速度。我进行了一些微基准测试(使用Kotlin 1.9,JVM 17),结果如下:



博客

书籍推荐

场景 Java static