Kotlin 中companion object {} 什么时候触发
在 Kotlin 中,companion object
的初始化触发时机是一个重要但容易被忽视的细节。以下是详细的解释:
1. 基本触发时机
companion object
的初始化发生在:
-
首次访问该类时(无论是访问伴生对象成员、创建类实例,还是通过反射访问类)
-
类似于 Java 的静态初始化块,但更灵活
-
class MyClass {companion object {init {println("Companion object 初始化!") }val CONST = "Hello"} }fun main() {println("程序启动")MyClass.CONST // 第一次访问 MyClass 时触发初始化 }
输出:
程序启动
Companion object 初始化!
2. 具体触发场景
以下操作都会触发 companion object
初始化:
(1) 直接访问伴生对象成员
MyClass.CONST // 触发
MyClass.Companion // 触发
(2) 创建类实例
val obj = MyClass() // 触发
(3) 通过反射访问类
MyClass::class // 触发(Kotlin 反射)
(4) 访问类的其他声明
MyClass::class // 触发(Kotlin 反射)
3. 不会触发的情况
以下操作不会触发初始化:
// 仅声明类型(未实际访问)
val clazz: Class<MyClass> = MyClass::class.java // 作为类型参数(未实例化或访问)
fun process(clazz: Class<*>) {}
process(MyClass::class.java) // 不会触发
4. 与 Java 静态初始化的对比
特性 | Kotlin companion object | Java static 块 |
---|---|---|
初始化时机 | 首次访问类时 | 类加载时 |
懒加载 | 是(按需初始化) | 否(类加载时强制初始化) |
线程安全 | 是(JVM 保证) | 是 |
5. 实际应用中的注意事项
(1) 性能优化
class HeavyInitialization {companion object {val HEAVY_RESOURCE = loadResource() // 避免在伴生对象中放重型初始化}
}// 改为懒加载
class BetterApproach {companion object {val heavyResource by lazy { loadResource() }}
}
(2) 循环依赖问题
class A {companion object {val VALUE = B.VALUE // 如果 B 的伴生对象也依赖 A,会导致栈溢出}
}class B {companion object {val VALUE = A.VALUE}
}
解决方案:使用 lazy
延迟初始化
class A {companion object {val VALUE by lazy { B.VALUE }}
}
6. 验证示例
class Test {companion object {init { println("伴生对象初始化") }}init { println("类实例初始化") }
}fun main() {println("阶段1")Test::class // 仅访问 KClass,不触发println("阶段2")Test.Companion // 触发初始化println("阶段3")Test() // 伴生对象已初始化,不会再次触发
}
输出:
阶段1
阶段2
伴生对象初始化
阶段3
类实例初始化
总结
companion object
在首次真正使用类时初始化(懒加载)- 比 Java 的静态初始化更灵活,但逻辑相似
- 适合存放类级别的常量、工厂方法等
- 避免在伴生对象中直接进行重型操作,建议用
by lazy