有时候我们需要创建与某个类稍微不一样的一个对象,但不用为此显示地声明一个子类。Java使用匿名内部类来处理这种情况,Kotlin用对象表达式和对象声明略微概括了这个概念。
创建一个继承自某个(或某些)类型的匿名类对象:
window.addMouseListener(object : MouseAdapter(){
override fun mouseClicked(e: MouseEvent){...}
override fun mouseEntered(e: MouseEvnent){...}
})
如果父类有构造方法,那么创建的时候必须传参数。如果有多个父类,可以用逗号隔开:
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 adHoc = object {
var x: Int = 0
var y: Int = 0
}
print(adHoc.x + adHoc.y)
}
注意匿名对象只能在本地和私有声明中用作类型。如果使用一个匿名对象作为public方法或public属性的返回值,那么该方法或属性的返回值的实际类型将会是匿名对象父类的声明类型,或者是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 // Works
val x2 = publicFoo().x // ERROR:调用会提示错误
}
}
因为publicFoo()
方法的返回值类型是Any
,所以无法调用x
属性。
和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++
}
})
// ...
}
Kotlin中声明单例非常简单:
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
// ...
}
val allDataProviders: Collection
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()
伴生对象的名称可以被忽略:
class MyClass {
companion object {
var name = "name"
}
}
//以下两种方式都可以调用伴生对象的成员
val x = MyClass.name
val x = MyClass.Companion.name
注意:尽管伴生对象的成员看起来像静态成员,在运行时它们仍然是真实对象的实例成员,并且能够实现接口:
interface Factory {
fun create(): T
}
class MyClass {
companion object : Factory {
override fun create(): MyClass = MyClass()
}
}
然而,在JVM平台上,如果使用@JvmStatic注释,可以把伴生对象的成员声明为真正的静态方法和字段,这个之后再细说。