背景Kotlin没有静态XX
那么对于Java
而言:
- 全局常量和变量的声明;
- 类方法的声明;
- 一些工具库无法直接使用(每次
new
);
Kotlin
之所以能抛弃静态成员,主要原因在于它允许包级属性和函数的存在,而且 Kotlin
为了维持与 Java
完全的兼容性,为静态成员提供了多种替代方案:
- 使用 包级属性和包级函数:主要用于
全局常量
和工具函数
; - 使用
伴生对象
:主要用于与类有紧密联系的变量和函数; - 使用
@JvmStatic
注解:与伴生对象搭配使用,将变量和函数声明为真正的 JVM 静态成员。
本文主要讲伴生对象。
伴生对象
什么是伴生对象?首先要知道什么是对象。Kotlin 中的对象指的是使用 object 关键字定义的 类型声明,一般用作单例模式和伴生对象。它让创建单例变得十分简单:
object Test {
val name = "Tom"
fun test() {
println("Hello")
}
}
反编译字节码
public final class Test {
public static final Test INSTANCE;
private static final String name = "Tom";
static {
new Test();
}
private Test() {
INSTANCE = this;
}
public static final String getName() {
return name;
}
public static final test() {
System.out.println("Hello")
}
}
能够发现,name
和 test()
虽然不是包级成员,但被编译成了静态成员。利用这个特性,Kotlin
允许在类中使用 companion object
创建伴生对象,用伴生对象的成员来代替静态成员。
一般使用场景
class Test {
companion object {
const val name = "Tom"
fun test() {
println("hello")
}
}
}
fun main() {
Test.test() // 不用创建实例直接访问函数
}
PS:上面的基本都是废话,已经到了2024年了谁还不知道这个。。。。。
interface Context {
public operator fun <E : Element> get(key: Key<E>): E?
public interface Key<E : Element>
public interface Element : Context {
public val key: Key<*>
public override operator fun <E : Element> get(key: Key<E>): E? =
@Suppress("UNCHECKED_CAST")
if (this.key == key) this as E else null
}
}
class TestContext : Context {
override fun <E : Context.Element> get(key: Context.Key<E>): E? {
return null
}
}
public interface ITask : Context.Element {
public companion object Key : Context.Key<ITask> //提供默认的接口实现
fun test()
}
public class Test : ITask {
override fun test() {
println("call test")
}
override val key: Context.Key<*> get() = ITask //ITask == ITask.Key
}
大家看看上面的ITask
的伴生对象Key
, 我们可以知道伴生对象也是可以有名字的。。。。
- 伴生对象是可以进行命名的;
- 伴生对象在使用的时候可以不用名字;
- 伴生对象如果实现接口,那么会提供默认实现;
PS:无论伴生对象是定义多么简单或者多么复杂的对象,它都只是一个对象而已。因此它具有对象的一切特征。但一个类只能有一个伴生对象,所以默认不需要指定名称。