object 的三种用法
Kotlin 的 object
关键字有三种用法:
- 对象声明 ,一般用来实现单例
- 伴生对象 ,类似 Java 的 static 关键字,也可以用于工厂方法模式
- 对象表达式 ,一般用来代替 Java 的匿名内部类
对象声明
object
的语义是这样的: 定义一个类并创建一个实例 。不管是对象声明,还是下面会说到的另外两种用法,都是遵循这一语义的。
作为对象声明,它可以直接用来实现单例模式:
object Singleton{
fun xxx(){}
}
话不多说,直接 Decompile 看 Java 代码:
public final class Singleton {
public static final Singleton INSTANCE;
public final void xxx() {
}
private Singleton() {
}
static {
Singleton var0 = new Singleton();
INSTANCE = var0;
}
}
从 Java 代码中可以看出来,显然这是一个单例模式。
- 私有构造函数
- 通过静态字段对外提供实例
- 静态代码块中直接初始化,线程安全 。
这里插播一个问题,static 代码块在何时执行?
。static 代码块就是在 初始化 阶段执行的。那么,哪些场景会触发类的初始化呢?有如下几种场景:
- 通过
new
实例化对象 - 读写一个类的静态字段
- 调用一个类的静态方法
- 对类进行反射调用
按照上面反编译出来的 Java 代码,获得单例对象的方法是 Singleton.INSTANCE
,即调用 Singleon
类的静态字段 INSTANCE
,就会触发类的初始化阶段,也就触发了 static 代码块的执行,从而完成了单例对象的实例化。同时,由于类加载过程天生线程安全,所以 Kotlin 的 object 单例活脱脱的就是一个线程安全的懒汉式单例(访问时初始化)。
此外,object 声明的单例类和普通类一样,可以实现接口,继承类,也可以包含属性,方法。但是它不能由开发者手动声明构造函数,从反编译出来的 Java 代码可以看到,它只有一个 private
构造函数。
所以,这对实际的业务场景是有一定限制的。对于需要携带参数的单例类,object 就有点力不从心了。当然也不难解决,模仿 Java 的写法就行了,这里以 DCL 模式为例。
class Singleton private constructor(private val param: Int) {
companion object {
@Volatile
private var instance: Singleton? = null
fun getInstance(property: Int) =
instance ?: synchronized(this) {
instance ?: Singleton(property).also { instance = it }
}
}
}
对象表达式的格式:
object[:若干个父类型,中间用逗号隔开]{
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main10)
val myObject = object : MyInterface {
override fun myPrint(i: Int) {
println("i的值是$i")
}
}
myObject.myPrint(100)
}
这个是对象表达式
object MyObject{
fun method() ="hello world
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
MyObject.method()
}
这个是对象声明语法。这个后面是有一个名字的,对象表达式是没有名字的。
这些对象可以有超类型:
object DefaultListener : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) { …… }
override fun mouseEntered(e: MouseEvent) { …… }
}
对象声明和对象表达式的差别:
- 对象表达式可以赋值给一个变量;但是对象是不可以的赋值给一个变量,对象声明本身是声明了一个对象,是不能直接放到等号右边的。
- 对象表达式是立刻初始化的或者执行的;对象声明是延迟初始化的,在首次访问的时候进行。
- 伴生对象是在其所对应的类被加载时初始化的,对应于Java的静态初始化。