Edit Page

对象表达式和对象声明

有些时候我们需要创建一个对某些类做了轻微改变的一个对象,而不用为了它显式地定义一个新的子类。 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
}

或许,我们需要的仅是无父类的一个对象,那么我们可以简单地写为:

val adHoc = object {
  var x: Int = 0
  var y: Int = 0
}
print(adHoc.x + adHoc.y)

就像Java的匿名内部类,在对象表达式里代码可以访问封闭的作用域 (但与Java不同的是,它能访问非final修饰的变量)

fun countClicks(window: JComponent) {
  var clickCount = 0
  var enterCount = 0

  window.addMouseListener(object : MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) {
      clickCount++
    }

    override fun mouseEntered(e: MouseEvent) {
      enterCount++
    }
  })
  // ...
}

对象声明

单例模式是一种非常有用的模式,而在Kotilin(在Scala之后)中很容易就能声明一个单例。

object DataProviderManager {
  fun registerDataProvider(provider: DataProvider) {
    // ...
  }

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

这被称为对象声明。如果有一个object关键字在名字前面,这不能再被称为一个_表达式_。 我们不能把这样的东西赋值给变量,但我们可以通过它的名字来引用它。这样的对象可以有父类型:

object DefaultListener : MouseAdapter() {
  override fun mouseClicked(e: MouseEvent) {
    // ...
  }

  override fun mouseEntered(e: MouseEvent) {
    // ...
  }
}

NOTE: 对象声明不能是本地的(即直接嵌套在函数里面),但它们可以被嵌套进另外的对象声明或者非内部类里。

伴生对象

一个对象声明在一个类里可以标志上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 这里 查看详情。

对象表达式与对象声明语义上的不同

这是一个在对象表达式与对象声明上重要的不同之处:

  • 当对象表达式被用到的时候,它会被立即执行(并且初始化)
  • 当对象声明被第一次访问的时候,它会被延迟(lazily)初始化
  • a companion object is initialized when the corresponding class is loaded (resolved), matching the semantics of a Java static initializer