Kotlin学习之-5.11 对象表达式和声明
有时候我们需要创建一个类的实例,并且扩展或者修改其中的一两个方法,但不想显式地声明一个子类。Java中是使用内部类来处理这种情况,Kotlin则使用对象表达式和对象声明扩展了这种概念。
对象表达式
创建一个内部类的对象,并且继承某些类型,我们这么写:
window.addMouseListener(object: MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
}
override fun mouseEntered(e: MouseEvent) {
}
})
如果父类型有构造器,必须传递合适的构造器参数。多个父类型可以使用逗号分隔的列表来描述,在冒号之后:
open class A(x: Int) {
public open val y: Int = x
}
interface B { ... }
val ab: A = object: A(1), B {
override val y = 15
}
有时候我们只需要一个对象,没有任何父类,我们可以这么写:
fun foo() {
val adHod = object {
var x: Int = 0
var y: Int = 0
}
print(adHod.x + adHoc.y)
}
注意匿名对象仅可以在局部和私有声明中被用来当做类型。如果使用一个匿名对象作为一个公用方法的返回值或者公用属性的类型,那么方法和属性的真正类型是声明成父类的匿名对象,或者Any
如果没有声明任何父类。给匿名对象添加的成员无法被访问。
class C {
// 私有方法,所以返回的类型是匿名对象类型
private fun foo() = object {
val x: String = "x"
}
// 共有方法,所以返回类型是Any
fun publicFoo() = object {
val x: String = "x"
}
fun bar() {
val x1 = foo().x // 可用
val x2 = publicFoo().x // 不可用,Unresolved reference 'x'
}
}
和Java中的匿名内部类一样,在对象表达式中的代码可以访问封装它的范围的变量。
fun countClicks(window: JComponet) {
var clickCount = 0
var enterCound = 0
window.addMouseListener(object: MouseAdapter() {
overfide fun mouseClicked(e: MouseEvent) {
clickCount++
}
overfide fun mouseEntered(e: MouseEvent) {
clickCount++
}
})
}
对象声明
单例是一种很有用的设计模式,在Kotlin中让定义单例更加容易:
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
}
val allDataProviders: Collection<DataProvider>
get() = // ...
}
这种方式叫作对象声明,并且它总有一个object
关键字。就想变量声明,一个对象声明不是一个表达式,不能用作赋值语句的右值。
要引用这个对象,直接使用对象的名字即可:
DataProviderManager.registerDataProvider( ... )
对象还可以有父类:
object DefaultListener : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
}
override fun mouseEntered(e: MouseEvent) {
}
}
注意:对象声明不能是局部的(例如,不能直接嵌套在函数里),但是可以嵌套在其他对象声明或者非内部类中。
伙伴对象
在一个类中的对象声明可以使用伙伴companion
关键字。
class MyClass {
companion object Factory {
fun create(): MyClass = MyClass()
}
}
伙伴对象的成员可以简单地使用类名作为描述符来调用:
val instance = MyClass.create()
伙伴对象的名字可以省略,这时使用Companion
关键字来访问:
class MyClass {
companion object {
}
}
val x = MyClass.Companion
注意,景观伙伴对象的成员和其他语言中的静态成员很像,但是在运行时他们仍然是实例成员的真正对象,例如,可以实现接口:
interface Factory<T> {
fun create(): T
}
class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}
但是,在JVM上如果是用@JvmStatic
注解,你仍然可以让伙伴对象的成员生成真正的静态方法和成员。 详见Java interoperability
对象表达式和声明的语法区别
在对象表达式和对象声明之间有一个重要的语法区别:
- 对象表达式在使用的时候,是立即执行的或者立即初始化的
- 对象声明是延迟初始化地,是在第一次访问对象的时候。
- 一个伙伴对象是在对应的类加载(或者解析)的时候初始化的,和Java中的静态初始化器对应。
PS,我会坚持把这个系列写完,有问题可以留言交流,也关注专栏Kotlin for Android Kotlin安卓开发