在 Kotlin 中,companion object
的扩展函数与普通函数(包括普通成员函数和普通扩展函数)有显著区别。以下是它们的核心差异和适用场景:
1. 定义位置与归属
特性 |
companion object 扩展函数 |
普通函数 |
定义位置 |
在类外部为伴生对象添加 |
在类内部(成员函数)或任意位置(扩展函数) |
归属关系 |
属于类的伴生对象,而非类实例 |
成员函数属于实例,普通扩展函数属于接收者类型 |
示例对比:
// companion object 扩展函数
class MyClass {
companion object
}
fun MyClass.Companion.extFunc() = println("扩展函数")
// 普通成员函数
class MyClass {
fun memberFunc() = println("成员函数")
}
// 普通扩展函数(非伴生对象)
fun MyClass.extFunc() = println("普通扩展函数")
2. 调用方式
特性 |
companion object 扩展函数 |
普通函数 |
调用主体 |
通过类名直接调用 |
成员函数需实例,普通扩展函数通过实例调用 |
语法 |
ClassName.func() |
instance.func() |
示例对比:
// companion object 扩展函数
MyClass.extFunc() // 直接通过类名调用
// 普通成员函数
val obj = MyClass()
obj.memberFunc() // 需要实例
// 普通扩展函数
obj.extFunc() // 需要实例
3. 访问权限
特性 |
companion object 扩展函数 |
普通函数 |
访问私有成员 |
只能访问伴生对象的私有成员 |
成员函数可访问类所有成员,普通扩展函数只能访问公有成员 |
上下文 |
无类实例上下文(相当于静态上下文) |
普通成员函数有 this 指向实例 |
示例对比:
class MyClass(private val secret: String) {
companion object {
private const val COMPANION_SECRET = "companion-secret"
}
fun memberFunc() {
println(secret) // 可访问实例私有属性
println(COMPANION_SECRET) // 可访问伴生对象私有属性
}
}
// companion object 扩展函数
fun MyClass.Companion.extFunc() {
println(COMPANION_SECRET) // 只能访问伴生对象的私有成员
// println(secret) // 编译错误:无法访问实例成员
}
// 普通扩展函数
fun MyClass.extFunc() {
// println(secret) // 编译错误:无法访问私有成员
// println(COMPANION_SECRET) // 编译错误:无法访问伴生对象私有成员
}
4. 使用场景
场景 |
companion object 扩展函数 |
普通函数 |
工具类方法 |
✅ 适合(如 StringUtils.parse() ) |
❌ 需实例,不直观 |
工厂模式 |
✅ 通过类名创建对象(MyClass.create() ) |
❌ 需先有工厂实例 |
实例操作 |
❌ 无法操作实例 |
✅ 主要用途 |
第三方库扩展 |
✅ 为已有类添加静态方法 |
✅ 为实例添加方法 |
典型用例:
// companion object 扩展:为 Android 的 Toast 添加静态方法
fun Toast.Companion.showShort(context: Context, text: String) {
makeText(context, text, Toast.LENGTH_SHORT).show()
}
// 调用:Toast.showShort(context, "Hello")
// 普通扩展:为 String 添加功能
fun String.addExclamation() = "$this!"
// 调用:"Hi".addExclamation()
5. 初始化时机
特性 |
companion object 扩展函数 |
普通函数 |
加载时机 |
首次访问类时初始化伴生对象 |
随实例创建或调用时执行 |
内存开销 |
类级别共享 |
实例级别(成员函数)或无状态(扩展函数) |
总结对比表
维度 |
companion object 扩展函数 |
普通成员函数 |
普通扩展函数 |
定义位置 |
类外部 |
类内部 |
任意位置 |
调用方式 |
ClassName.func() |
instance.func() |
instance.func() |
访问权限 |
仅伴生对象成员 |
全实例成员 |
仅公有成员 |
典型用途 |
静态工具方法、工厂模式 |
实例行为封装 |
增强已有类功能 |
内存分配 |
类级别(单次初始化) |
每实例占用 |
无状态(不占内存) |
选择建议
- 需要 通过类名直接调用 且 不依赖实例状态 → 用
companion object
扩展函数
- 需要 操作具体实例数据 → 用普通成员函数
- 需要 为无法修改的类添加功能 → 用普通扩展函数