对象表达式和声明

有时候我们想要创建一个对当前类有一点小修改的对象,但不想重新声明一个子类。java 用匿名内部类的概念解决这个问题。Kotlin 用对象表达式和对象声明巧妙的实现了这一概念。

对象表达式

通过下面的方式可以创建继承自某种(或某些)匿名类的对象:

1. window.addMouseListener(object: MouseAdapter () {
2. override fun mouseClicked(e: MouseEvent) {
3. //...
4. }
5. })

如果父类有构造函数,则必须传递相应的构造参数。多个父类可以用逗号隔开,跟在冒号后面:

1. open class A(x: Int) {
2. public open val y: Int = x
3. }

5. interface B { ... }

7. val ab = object : A(1), B {
8. override val y = 14
9. }

有时候我们只是需要一个没有父类的对象,我们可以这样写:

1. val adHoc = object {
2. var x: Int = 0
3. var y: Int = 0
4. }

6. print(adHoc.x + adHoc.y)

像 java 的匿名内部类一样,对象表达式可以访问闭合范围内的变量 (和 java 不一样的是,这些变量不用是 final 修饰的)

1. fun countClicks(window: JComponent) {
2. var clickCount = 0
3. var enterCount = 0
4. window.addMouseListener(object : MouseAdapter() {
5. override fun mouseClicked(e: MouseEvent) {
6. clickCount++
7. }
8. override fun mouseEntered(e: MouseEvent){
9. enterCount++
10. }
11. })
12. }

对象声明

单例模式是一种很有用的模式,Kotln 中声明它很方便:

1. object DataProviderManager {
2. fun registerDataProvider(provider: DataProvider) {
3. // ...
4. }

6. val allDataProviders: Collection<DataProvider>
7. get() = // ...
8. }

这叫做对象声明,跟在 object 关键字后面是对象名。和变量声明一样,对象声明并不是表达式,而且不能作为右值用在赋值语句。

想要访问这个类,直接通过名字来使用这个类:

1. DataProviderManager.registerDataProvider(...)

这样类型的对象可以有父类型:

1. object DefaultListener : MouseAdapter() {
2. override fun mouseClicked(e: MouseEvent) {
3. // ...
4. }

6. override fun mouseEntered(e: MouseEvent) {
7. // ...
8. }
9. }

注意:对象声明不可以是局部的(比如不可以直接在函数内部声明),但可以在其它对象的声明或非内部类中进行内嵌入

伴随对象

在类声明内部可以用 companion 关键字标记对象声明:

1. class MyClass {
2. companion object Factory {
3. fun create(): MyClass = MyClass()
4. }
5. }

伴随对象的成员可以通过类名做限定词直接使用:

1. val instance = MyClass.create()

在使用了 companion 关键字时,伴随对象的名字可以省略:

1. class MyClass {
2. companion object {

4. }
5. }

注意,尽管伴随对象的成员很像其它语言中的静态成员,但在运行时它们任然是真正对象的成员实例,比如可以实现接口:

1. interface Factory<T> {
2. fun create(): T
3. }

5. class MyClass {
6. companion object : Factory<MyClass> {
7. override fun create(): MyClass = MyClass()
8. }
9. }

如果你在 JVM 上使用 @JvmStatic 注解,你可以有多个伴随对象生成为真实的静态方法和属性。参看 java interoperabillity。

对象表达式和声明的区别

他俩之间只有一个特别重要的区别:

对象表达式在我们使用的地方立即初始化并执行的

对象声明是懒加载的,是在我们第一次访问时初始化的。

​ 伴随对象是在对应的类加载时初始化的,和 Java 的静态初始是对应的。