伴生对象是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
}
七、总结
-
概念:伴生对象是类内部的特殊对象,与类关联,可以访问类的私有成员
-
基本使用 :通过
companion object
声明,通过类名直接访问成员 -
使用场景:
- 替代Java静态成员
- 实现工厂模式
- 创建工具类
- 实现接口
-
高级特性:
- 可命名伴生对象
- 支持扩展函数
- 可以包含属性和复杂逻辑
-
Java互操作:
- 通过
Companion
字段访问 - 使用
@JvmStatic
和@JvmField
优化
- 通过
-
注意事项:
- 初始化时机
- 与对象表达式的区别
- 继承行为
- 性能考虑
伴生对象是Kotlin中非常灵活的特性,合理使用可以使代码更加简洁、表达力更强,同时保持与Java的良好互操作性。
更多分享
- 一文吃透Kotlin中冷流(Clod Flow)和热流(Hot Flow)
- 一文带你吃透Kotlin协程的launch()和async()的区别
- Kotlin 委托与扩展函数------新手入门
- Kotlin 作用域函数(let、run、with、apply、also)的使用指南
- 一文带你吃透Kotlin中 lateinit 和 by lazy 的区别和用法
- Kotlin 扩展方法(Extension Functions)使用详解
- Kotlin 中 == 和 === 的区别
- Kotlin 操作符与集合/数组方法详解------新手指南
- Kotlin 中 reified 配合 inline 不再被类型擦除蒙蔽双眼
- Kotlin Result 类型扩展详解 ------ 新手使用指南