1. 概述

在 Kotlin 中,当我们需要定义一些不依赖类实例的类级成员时,通常会使用 Companion Object。Kotlin 编译器会确保每个类中最多只有一个 Companion Object 实例。对于熟悉 Java 或 C# 的开发者来说,Companion Object 类似于 static 成员的实现方式。

例如,在实现工厂模式(如静态工厂方法或抽象工厂)时,Companion Object 就非常有用,因为它可以作为类的“静态上下文”来集中管理对象的创建逻辑。

2. 声明 Companion Object

Companion Object 的声明方式如下:

class ClassName {
    companion object {
        const val propertyName: String = "Something..."
        fun funName() {
            //...
        }
    }
}

在类名后直接通过 .属性名.方法名() 的方式调用:

val property = ClassName.propertyName
ClassName.funName()

同时,在类内部也可以直接访问这些成员,无需加类名:

class ClassName {
    fun anotherFun() {
        println(propertyName)
    }

    companion object {
        const val propertyName: String = "Something..."
        fun funName() {
            //...
        }
    }
}

注意: Companion Object 成员会在首次被调用时初始化,且是在其所属类的实例化之后完成初始化。

3. 命名 Companion Object

默认情况下,Companion Object 的名字是 Companion,但我们也可以给它起一个更具有语义化的名字,比如 FactoryUtils 等。

以工厂方法模式为例,我们可以通过命名 Companion Object 来提升代码可读性:

class MyClass {
    companion object Factory {
        fun createInstance(): MyClass = MyClass()
    }
}

调用时可以使用类名 + Companion 名字的方式:

val instance = MyClass.Factory.createInstance()

注意: 每个类只能有一个 Companion Object,不能定义多个。

4. 继承与 Companion Object

Companion Object 不能被继承,但它可以继承其他类或实现接口。这使得它在某些场景下比 Java 的 static 更灵活。

举个例子,我们可以利用这个特性来实现抽象工厂模式(Abstract Factory Pattern):

首先定义接口和抽象类:

interface Theme {
    fun someFunction(): String
}

abstract class FactoryCreator {
    abstract fun produce(): Theme
}

然后分别实现两个具体的类和它们的 Companion Object:

class FirstRelatedClass : Theme {
    companion object Factory : FactoryCreator() {
        override fun produce() = FirstRelatedClass()
    }

    override fun someFunction(): String {
        return "I am from the first factory."
    }
}

class SecondRelatedClass : Theme {
    companion object Factory : FactoryCreator() {
        override fun produce() = SecondRelatedClass()
    }

    override fun someFunction(): String {
        return "I am from the second factory."
    }
}

最后在 main 函数中使用抽象工厂:

fun main() {
    val factoryOne: FactoryCreator = FirstRelatedClass.Factory
    println(factoryOne.produce().someFunction())

    val factoryTwo: FactoryCreator = SecondRelatedClass.Factory
    println(factoryTwo.produce().someFunction())
}

这篇文章详细解释了 Kotlin 中的抽象工厂模式

5. Java 互操作性

虽然 Kotlin 的 Companion Object 可以继承类和接口,但在 Java 中无法直接访问这些特性。为了在 Java 中更好地使用这些成员,我们需要使用 @JvmStatic 注解。

例如:

class MyClass {
    companion object {
        @JvmStatic
        val propertyName: String = "Something..."
    }
}

这样在 Java 中就可以像调用静态字段一样访问它:

MyClass.propertyName;

更多内容可以参考:在 Java 中访问 Kotlin 的 Companion Object

6. 接口中的 Companion Object

Kotlin 的接口也可以包含 Companion Object。这在定义常量或辅助函数时非常有用,特别是这些常量或函数与接口本身语义相关时。

例如:

interface MyInterface {
    companion object {
        const val PROPERTY = "value"
    }
}

这样我们就可以直接通过接口名访问:

println(MyInterface.PROPERTY)

7. 总结

Companion Object 是 Kotlin 中用于定义类级成员的重要机制。它不仅提供了类似 Java 中 static 的功能,还支持继承和接口实现,大大增强了其灵活性。

本文涉及的代码示例均可在 GitHub 仓库 找到。


原始标题:Kotlin Companion Object