companion object — Kotlin 语言的一种机制,用于替代 Java 中的 static 关键字来声明类的静态成员。Kotlin 没有内置的 static,而是在类内部使用带有 companion 修饰符的 object 声明。根据 JetBrains, 2025,companion object 允许通过类名调用方法和属性,使其成为 Java 生态系统中静态成员的完整对应物。
要点
companion object — 是 Kotlin 语言中一种特殊的 object declaration,用 companion 关键字标记。它允许声明属于类而非其实例的成员——即 Java 术语中的静态成员。与 Java 中 static 是单个字段和方法的修饰符不同,在 Kotlin 中静态成员被分组在一个伴生对象内。
Kotlin 中的每个类只能包含一个 companion object。该对象的成员可以通过类名访问,无需创建实例:MyClass.method()。同时从字节码的角度来看,companion object 被编译为单独的内部类,其方法在使用 @JvmStatic 注解时成为外部类的静态方法。
根据 Google I/O 2024,companion object 已成为现代 Kotlin Android 应用中工厂方法、常量和实用函数的主要模式。开发者选择它而不是带有包函数的工具类,因为 companion object 保持了与拥有类的逻辑联系。
使用 companion object 对静态上下文进行分组——常量、工厂、辅助方法,这些方法在逻辑上属于类但不需要实例。
companion object 的基本语法很简单:companion 关键字放在类内部的 object 声明之前。如果未指定名称,该对象将获得 Companion 名称,可以显式或隐式引用。
class User {
companion object {
const val TABLE_NAME = "users"
fun create(name: String): User {
return User(name)
}
}
val name: String
constructor(name: String) { this.name = name }
}
// 通过类名访问
val table = User.TABLE_NAME
val user = User.create("Alice")
在上面的示例中,TABLE_NAME 和 create() 可以通过 User.TABLE_NAME 和 User.create() 访问。注意原始类型常量的 const 修饰符——它保证在编译阶段嵌入值,类似于 Java 中对原始类型和 String 的 static final。
如果 companion object 没有名称,可以通过自动名称 Companion 访问:User.Companion.TABLE_NAME。在实践中这很少需要,但在从 Java 代码调用或使用反射时很有用。
// 两个调用是等效的
User.create("Bob")
User.Companion.create("Bob")
// 获取对 companion object 的引用
val companion: User.Companion = User
companion object 可以有名称,这提高了代码的可读性并允许通过易理解的名称引用它。Factory 是最常见的用作工厂的 companion object 名称。
class HttpResponse {
companion object Factory {
fun ok(body: String): HttpResponse = HttpResponse(200, body)
fun notFound(): HttpResponse = HttpResponse(404, "Not Found")
fun serverError(): HttpResponse = HttpResponse(500, "Internal Error")
}
}
// 通过 Factory 名称访问
val response = HttpResponse.Factory.ok("data")
命名 companion object 有助于记录静态成员的用途。在重构中,名称使代码自文档化——开发者立即看到 Factory 创建 HttpResponse 的实例。
一个类只能包含一个 companion object。这是与 Java 的根本区别,Java 中可以声明任意数量的静态字段和方法而无需分组。如果需要多个逻辑组的静态成员,请使用不带 companion 的嵌套 object 声明。
根据 Kotlin 官方文档(Kotlin Docs, 2025),限制为一个 companion object 是由语言设计驱动的:静态成员应该与类紧密关联,一个伴生对象提供了这种关联的清晰边界。如果静态成员过多——这是类需要重构的信号。
在 Kotlin 中,接口也可以包含 companion object。这允许直接在接口内部定义静态方法和常量,这在 Java 中是不可能的。这种方法常用于声明与接口相关的常量或工厂方法。
interface ApiService {
companion object {
const val BASE_URL = "https://api.example.com"
const val TIMEOUT_MS = 5000
fun create(client: OkHttpClient): ApiService =
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.build()
.create(ApiService::class.java)
}
}
在这个例子中,BASE_URL、TIMEOUT_MS 和 create() 属于接口 ApiService,而不是它的实现。这很方便:创建 API 服务实例所需的所有常量和工厂逻辑都在一个地方——接口本身内部。
接口的实现不会继承 companion object 的成员——它们只能通过接口名称调用:ApiService.BASE_URL。
从 Java 代码调用 companion object 的方法时,它们默认作为嵌套类 Companion 的方法可用,而不是作为外部类的静态方法。要将它们导出为 Java 的真正 static 方法和字段,请对方法使用 @JvmStatic 注解,对字段使用 @JvmField 注解。
| 注解 | 应用 | Java 中的结果 |
|---|---|---|
| @JvmStatic | Companion object 的方法 | 静态方法:ClassName.method() |
| @JvmField | Companion object 的字段 | 静态字段:ClassName.field |
| const | 原始类型和 String | 内联常量:编译器替换值 |
| 无注解 | 默认方法/字段 | Companion.method() / Companion.field |
class MathUtils {
companion object {
const val PI = 3.14159
@JvmStatic
fun square(x: Int): Int = x * x
@JvmField
val TAG: String = "MathUtils"
}
}
// 在 Java 中作为 MathUtils.square(5), MathUtils.TAG 调用
建议对所有需要从 Java 代码访问的公共 companion object 方法使用 @JvmStatic。const 仅适用于原始类型和 String——对于其他类型请使用 @JvmField。
在实际项目中,companion object 用于几种标准场景。工厂方法是最常见的模式:使用 companion object 中命名的工厂方法替代具有不同签名的多个构造函数。
sealed class NetworkResult<out T> {
data class Success<out T>(val data: T) : NetworkResult<T>()
data class Error(val message: String) : NetworkResult<Nothing>()
companion object {
fun loading<T>(): NetworkResult<T> =
Loading()
}
}
private class Loading<T> : NetworkResult<T>()
NetworkResult 中的 companion object 提供工厂方法 loading(),用于创建具有正确泛型类型的 Loading 实例。如果没有这个方法,您必须直接通过 Loading() 创建实例,这会暴露内部实现。
companion object 通常用于存储类特定的常量。使用 const val 声明的常量在编译阶段嵌入,在运行时实现零开销。
class UserRepository {
companion object {
private const val TAG = "UserRepository"
private const val CACHE_SIZE = 100
private const val DEFAULT_PAGE_SIZE = 20
}
}
常见问题
ordinary object 是一个独立于类存在的完整单例。companion object 是类内部的一个对象,其成员通过外部类的名称调用。在字节码中,companion object 成为静态嵌套类,而普通对象成为独立的单例类。
不可以,companion object 不会被子类继承。如果类 B 继承自类 A,B.method() 不会调用类 A 的 companion object 中的方法——需要通过 A.method() 访问。这与 Java 中静态方法的行为一致。
默认情况下——通过 ClassName.Companion.method()。要以静态方法调用,请向 companion object 的方法添加 @JvmStatic。对于字段,请使用 @JvmField 或使用 const val 声明原始类型。
Kotlin 的开发者决定放弃 static,采用 companion object 以实现对象操作的统一。Java 中的 static 违反了 OOP 原则,因为静态方法不与实例绑定。companion object 是一等对象,可以传递、使用扩展函数扩展以及实现接口。
使用 const val 处理原始类型和 String 时——零开销,值嵌入在字节码中。对于方法,如果方法不是 inline 的,则没有开销。在大多数情况下,companion object 对性能没有可衡量的影响。仅对从 Java 调用的公共 API 使用 @JvmStatic。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。