目录
1. 前言
object
关键字在 kotlin 中有两种使用场景:对象表达式 (object expressions)和对象声明(object declarations)。
2. 正文
2.1 对象表达式(object expressions)
创建继承某个(或某些)类型的匿名类的对象,这些类型可以是接口(以给 Button
设置点击事件为例):
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button = findViewById<Button>(R.id.button)
button.setOnClickListener(object: View.OnClickListener {
override fun onClick(v: View?) {
Log.d(MainActivity::class.java.simpleName, "click button")
}
})
}
}
可以是抽象类(以属性动画中的 AnimatorListenerAdapter
为例):
val valueAnimator = ValueAnimator.ofInt(0, 5)
valueAnimator.addListener(object: AnimatorListenerAdapter() {
override fun onAnimationStart(animation: Animator?) {
super.onAnimationStart(animation)
Log.d(MainActivity::class.java.simpleName, "onAnimationStart")
}
override fun onAnimationEnd(animation: Animator?) {
super.onAnimationEnd(animation)
Log.d(MainActivity::class.java.simpleName, "onAnimationEnd")
}
})
valueAnimator.duration = 2000L
valueAnimator.start()
那么,超类型是接口和抽象类的区别和联系是什么呢?
相同的地方是都是要把 object
关键字放在前面,后面加 :,再加上接口或者抽象类,在花括号里重写方法;
不同的地方是接口后面没有小括号,而抽象类后面必须有小括号。
从上面的对象表达式的用法可以看到,它和 java 中的匿名内部类的作用是一样的。
2.2 对象声明(object declarations)
2.2.1 对象声明
对象声明,就是在 kotlin 中声明单例的方式:
object DataRepository {
var data = listOf(1,2,3)
}
对象声明的特点是 object
关键字后面跟着一个名称。就像声明一个变量那样,但是对象声明不是一个表达式,不能把对象声明放在赋值语句的右边。
利用 AndroidStudio 的 Show Kotlin Bytecode 功能,看一下对应的 .java 文件:
public final class DataRepository {
@NotNull
private static List data;
public static final DataRepository INSTANCE;
@NotNull
public final List getData() {
return data;
}
public final void setData(@NotNull List var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
data = var1;
}
static {
DataRepository var0 = new DataRepository();
INSTANCE = var0;
data = CollectionsKt.listOf(new Integer[]{1, 2, 3});
}
}
可以看到确实是一个单例模式的形式,是饿汉式的单例。这种形式的单例是线程安全的。
使用对象声明的方式:直接使用名称调用即可。代码如下:
Log.d(MainActivity::class.java.simpleName, DataRepository.data.toString())
需要注意的是,对象声明不能在局部作用域(即直接嵌套在函数内部),但是可以嵌套在其他对象声明或非内部类中。
对象声明不能在局部作用域的例子:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
object Singleton { }// 这里编译报错:Named object 'Singleton' is a singleton and cannot be local. Try to use anonymous object instead
}
}
对象声明嵌套在其他对象声明的例子,这里是把 Singleton
对象声明嵌套在 DataRepository
中:
object DataRepository {
var data = listOf(1,2,3)
object Singleton {
}
}
对象声明在非内部类中的例子:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
object Singleton {}
}
对象声明不可以在内部类中:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
inner class InnerClass { // 在 kotlin 中声明内部类,必须显式地加上 inner 关键字
object Singleton {} // 编译报错:Object is not allowed here
}
}
所以,可以看到:使用对象声明,可以非常容易地声明一个单例。
思考一下:对象声明和普通类的实例有什么区别?
对象声明不允许有构造方法,而普通类的实例获取需要构造方法。
2.2.2 伴生对象(companion object)
类内部的对象声明可以用 companion
关键字标记,这就是伴生对象。伴生对象在一个类中,只能声明一个。
这里使用自定义的 Application 作为例子:
class App : Application() {
companion object {
private var instance: Application? = null
fun instance() = instance!!
}
override fun onCreate() {
super.onCreate()
instance = this
}
}
这个伴生对象的成员,可以通过类名. 的方式调用:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val instance = App.instance()
}
}
在类中定义的对象声明和伴生对象的区别是什么呢?
对于对象声明,object 关键字后面必须有一个名字;而伴生对象的名称可以省略,这种情况下它的名称就是 Companion,也可以显式地有一个名字。
对于对象声明,在类内部可以声明多个;而伴生对象只能声明一个。
对于对象声明,获取它的成员必须是所在类的名字.对象声明的名字.成员名;而伴生对象则是所在类的名字.成员名即可。
3. 最后
这篇文字简单介绍了 object 关键字的使用,希望对大家有帮助。