Kotlin 伴生对象(Companion Object)详解 —— 使用指南

伴生对象是Kotlin中一个独特且强大的特性,它提供了类级别的功能和属性,同时保持了与Java的良好互操作性。

一、概念与基本使用

1.1 什么是伴生对象

伴生对象是声明在类内部的对象,用companion object关键字标记。它与外部类关联,可以访问类的私有成员,相当于类的"静态"成员容器。

kotlin 复制代码
class MyClass {
    companion object {
        const val CONSTANT = "Constant Value"
        
        fun create(): MyClass = MyClass()
    }
}

1.2 基本用法

kotlin 复制代码
class DatabaseClient {
    companion object {
        private var instance: DatabaseClient? = null
        
        fun getInstance(): DatabaseClient {
            if (instance == null) {
                instance = DatabaseClient()
            }
            return instance!!
        }
    }
    
    fun connect() { println("Connected") }
}

fun main() {
    // 通过类名直接访问伴生对象成员
    val client = DatabaseClient.getInstance()
    client.connect()
}

二、使用场景

2.1 替代Java静态成员

kotlin 复制代码
class MathUtils {
    companion object {
        fun add(a: Int, b: Int) = a + b
        const val PI = 3.14159
    }
}

fun main() {
    println(MathUtils.add(2, 3))  // 5
    println(MathUtils.PI)         // 3.14159
}

2.2 工厂方法

kotlin 复制代码
class User private constructor(val name: String) {
    companion object {
        fun create(name: String): User {
            return User(name.trim())
        }
        
        fun createWithDefault(): User {
            return User("Guest")
        }
    }
}

fun main() {
    val user1 = User.create("Alice")
    val user2 = User.createWithDefault()
}

2.3 实现接口

kotlin 复制代码
interface Factory<T> {
    fun create(): T
}

class MyClass {
    companion object : Factory<MyClass> {
        override fun create(): MyClass {
            return MyClass()
        }
    }
}

fun <T> createInstance(factory: Factory<T>): T {
    return factory.create()
}

fun main() {
    val instance = createInstance(MyClass)
}

三、高级特性

3.1 伴生对象名称

可以为伴生对象指定名称,未指定时默认名称为Companion

kotlin 复制代码
class MyClass {
    companion object Named {
        fun method() { println("Named companion") }
    }
}

fun main() {
    MyClass.Named.method()  // 使用名称访问
    MyClass.method()        // 也可以直接访问
}

3.2 扩展函数

可以为伴生对象定义扩展函数:

kotlin 复制代码
class MyClass {
    companion object
}

fun MyClass.Companion.extensionFunc() {
    println("Extension function on companion")
}

fun main() {
    MyClass.extensionFunc()
}

3.3 伴生对象中的属性

kotlin 复制代码
class Configuration {
    companion object {
        private var _version: String = "1.0"
        
        var version: String
            get() = _version
            set(value) { _version = value }
            
        val buildNumber: Int by lazy { 
            // 复杂初始化
            Random.nextInt(1000) 
        }
    }
}

fun main() {
    println(Configuration.version)  // 1.0
    Configuration.version = "2.0"
    println(Configuration.buildNumber)
}

四、与Java互操作

4.1 从Java访问伴生对象

Kotlin伴生对象在Java中表现为一个名为Companion的静态字段(如果伴生对象有名称,则使用该名称)。

java 复制代码
// Java代码
public class JavaClient {
    public static void main(String[] args) {
        // 访问Kotlin伴生对象
        MyClass.Companion.method();
        
        // 如果伴生对象有名称
        // MyClass.Named.method();
        
        // 对于const val属性
        String constant = MyClass.CONSTANT;
    }
}

4.2 使用@JvmStatic和@JvmField优化

为了使Kotlin伴生对象成员在Java中更像静态成员,可以使用注解:

kotlin 复制代码
class KotlinClass {
    companion object {
        @JvmStatic
        fun staticMethod() { println("Static method") }
        
        @JvmField
        val staticField = "Static Field"
        
        const val CONSTANT = "Constant"
    }
}

在Java中使用:

java 复制代码
// Java代码
public class JavaClient {
    public static void main(String[] args) {
        KotlinClass.staticMethod();  // 可以直接调用
        String field = KotlinClass.staticField;  // 直接访问字段
        String constant = KotlinClass.CONSTANT;  // const val自动为静态
    }
}

4.3 伴生对象实现接口时的Java互操作

kotlin 复制代码
interface Logger {
    fun log(message: String)
}

class MyClass {
    companion object : Logger {
        override fun log(message: String) {
            println(message)
        }
    }
}

// Java中使用
public class JavaClient {
    public static void main(String[] args) {
        MyClass.Companion.log("Message from Java");
    }
}

五、注意事项

5.1 伴生对象的初始化时机

伴生对象在对应的类被加载时初始化(通常是第一次访问时),且只初始化一次。

kotlin 复制代码
class InitializationDemo {
    companion object {
        init {
            println("Companion object initialized")
        }
        
        fun method() { println("Method called") }
    }
}

fun main() {
    println("Before access")
    InitializationDemo.method()  // 此时伴生对象才会初始化
}

5.2 伴生对象与对象表达式的区别

  • 伴生对象是类级别的单例,与类关联
  • 对象表达式是立即执行的匿名对象
kotlin 复制代码
class MyClass {
    companion object {
        // 类级别的单例
    }
    
    fun createAnonymousObject(): Any {
        return object {
            // 每次调用都会创建新实例
            val prop = 42
        }
    }
}

5.3 继承中的伴生对象

伴生对象不会被继承,每个类都有自己的伴生对象实例。

kotlin 复制代码
open class Parent {
    companion object {
        fun method() { println("Parent companion") }
    }
}

class Child : Parent() {
    companion object {
        fun method() { println("Child companion") }
    }
}

fun main() {
    Parent.method()  // Parent companion
    Child.method()   // Child companion
}

5.4 性能考虑

伴生对象中的属性和方法不是真正的静态成员(除非使用@JvmStatic),访问时会有轻微的性能开销。

六、实际应用示例

6.1 单例模式

kotlin 复制代码
class Singleton private constructor() {
    companion object {
        @Volatile private var instance: Singleton? = null
        
        fun getInstance(): Singleton {
            return instance ?: synchronized(this) {
                instance ?: Singleton().also { instance = it }
            }
        }
    }
    
    fun doWork() { println("Working...") }
}

fun main() {
    Singleton.getInstance().doWork()
}

6.2 构建器模式

kotlin 复制代码
class Car private constructor(
    val model: String,
    val color: String,
    val year: Int
) {
    companion object Builder {
        private var model: String = ""
        private var color: String = "Black"
        private var year: Int = 2023
        
        fun model(model: String) = apply { this.model = model }
        fun color(color: String) = apply { this.color = color }
        fun year(year: Int) = apply { this.year = year }
        
        fun build(): Car {
            require(model.isNotEmpty()) { "Model must be specified" }
            return Car(model, color, year)
        }
    }
}

fun main() {
    val car = Car.Builder
        .model("Tesla")
        .color("Red")
        .year(2022)
        .build()
    
    println("${car.model} ${car.color} ${car.year}")
}

6.3 工具类

kotlin 复制代码
class StringUtils {
    companion object {
        fun isPalindrome(s: String): Boolean {
            return s == s.reversed()
        }
        
        fun capitalize(s: String): String {
            return s.replaceFirstChar { it.uppercase() }
        }
    }
}

fun main() {
    println(StringUtils.isPalindrome("madam"))  // true
    println(StringUtils.capitalize("hello"))    // Hello
}

七、总结

  1. 概念:伴生对象是类内部的特殊对象,与类关联,可以访问类的私有成员

  2. 基本使用 :通过companion object声明,通过类名直接访问成员

  3. 使用场景

    • 替代Java静态成员
    • 实现工厂模式
    • 创建工具类
    • 实现接口
  4. 高级特性

    • 可命名伴生对象
    • 支持扩展函数
    • 可以包含属性和复杂逻辑
  5. Java互操作

    • 通过Companion字段访问
    • 使用@JvmStatic@JvmField优化
  6. 注意事项

    • 初始化时机
    • 与对象表达式的区别
    • 继承行为
    • 性能考虑

伴生对象是Kotlin中非常灵活的特性,合理使用可以使代码更加简洁、表达力更强,同时保持与Java的良好互操作性。

更多分享

  1. 一文吃透Kotlin中冷流(Clod Flow)和热流(Hot Flow)
  2. 一文带你吃透Kotlin协程的launch()和async()的区别
  3. Kotlin 委托与扩展函数------新手入门
  4. Kotlin 作用域函数(let、run、with、apply、also)的使用指南
  5. 一文带你吃透Kotlin中 lateinit 和 by lazy 的区别和用法
  6. Kotlin 扩展方法(Extension Functions)使用详解
  7. Kotlin 中 == 和 === 的区别
  8. Kotlin 操作符与集合/数组方法详解------新手指南
  9. Kotlin 中 reified 配合 inline 不再被类型擦除蒙蔽双眼
  10. Kotlin Result 类型扩展详解 ------ 新手使用指南
相关推荐
CYRUS_STUDIO11 小时前
利用 Linux 信号机制(SIGTRAP)实现 Android 下的反调试
android·安全·逆向
CYRUS_STUDIO11 小时前
Android 反调试攻防实战:多重检测手段解析与内核级绕过方案
android·操作系统·逆向
iOS阿玮12 小时前
永远不要站在用户的对立面,挑战大众的公知。
uni-app·app·apple
黄林晴15 小时前
如何判断手机是否是纯血鸿蒙系统
android
火柴就是我15 小时前
flutter 之真手势冲突处理
android·flutter
法的空间15 小时前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
循环不息优化不止15 小时前
深入解析安卓 Handle 机制
android
恋猫de小郭16 小时前
Android 将强制应用使用主题图标,你怎么看?
android·前端·flutter
jctech16 小时前
这才是2025年的插件化!ComboLite 2.0:为Compose开发者带来极致“爽”感
android·开源
用户20187928316716 小时前
为何Handler的postDelayed不适合精准定时任务?
android