提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
本文就来介绍 伴生对象(companion object)的知识点。
一. 伴生对象(companion object)的诞生
Kotlin 的一个特性:没有静态成员。
我们都知道,静态成员在 Java 中有很大的作用,因为 Java 没有全局变量,也不存在包级函数,一切属性和方法都是在类里面,所以在写一些工具函数和全局变量时,都需要用到 static 关键字修饰的静态成员。
Kotlin 之所以能抛弃静态成员,主要原因在于它允许包级属性和函数的存在,而且 Kotlin 为了维持与 Java 完全的兼容性,为静态成员提供了多种替代方案:
- 使用 包级属性和包级函数:主要用于 全局常量 和 工具函数 ;
- 使用 伴生对象:主要用于与类有紧密联系的变量和函数;
- 使用 @JvmStatic 注解:与伴生对象搭配使用,将变量和函数声明为真正的 JVM 静态成员。
二. companion 的特性
1. 声明伴生对象的语法:
companion object ObjectName : [0~N个父类型] { //伴生对象类体 }
// ObjectName 可省略
2. 特点:
-
伴生对象相当于类的对象,可直接通过类名访问伴生对象的成员;
-
每个类最多定义一个伴生对象;
-
kotlin 没有 static 关键字,伴生对象是为弥补 kotlin 没有 static 修饰的静态成员的不足;
-
@JvmStatic 注解只能用在伴生对象里,修饰伴生对象内的属性和函数。
三. companion 的实现
在 Kotlin 中,调用 Java 的 static 方法和调用 Kotlin 的 companion object 方法是一样的:
JavaClass.staticFun() // 调用 Java 中静态方法
KotlinClass.companionFun() // 调用 Kotlin 中伴生对象的方法
而在 Java 中,调用 static 方法和 Kotlin 中伴生 companion object 方法,有点不同:
JavaClass.staticFun(); // Java 中调用 static 方法
KotlinClass.Companion.companionFun(); // 在Java中,调用伴生companion
从 Java 的调用可以发现,companion object 的 JVM 字节码,是一个声明了一个叫 Companion 的 static 变量。 而在 Kotlin 中调用一致,其实是编译器的特殊处理结果。
来反编译一下 KotlinClass
,可以看到:
// KotlinClass.class
public final class KotlinClass {
public static final Companion companion = new Companion(null);
}
伴生对象 反编译后有如下代码:
// KotlinClass$Companion.class
public final class KotlinClass$Companion {
private DownloadExecutor$Companion() {
}
public /* synthetic */ DownloadExecutor$Companion(DefaultConstructorMarker $constructor_marker) {
this();
}
public final void companionFun() {
}
}
可以看到,Companion 是一个叫 KotlinClass$Companion
的类的实例,带 $ 符号表示这个类是 KotlinClass 的内部类,名字叫 Companion,所以在KotlinClass
中直接new Companion(null)
即可。
从上面的反编译代码看,实例化 Companion 使用的是一个带 DefaultConstructorMarker 入参的构造器。
出现的场景是,若 Kotlin 编译器生成特殊构造器,就会带这个参数。上面这段代码里 Kotlin 希望能实例化 Companion 类,但又不想声明一个 public 的构造器,就声明了一个特殊的构造器。DefaultConstructorMarker 值永远为 null。
DefaultConstructorMarker 的另一个场景是:带默认参数的构造器。