Kotlin中的object和companion object关
一. object
1. object 表达式
可以创建匿名内部类
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
// ...
}
override fun mouseEntered(e: MouseEvent) {
// ...
}
})
通过object 创建的匿名内部类可以继承父类,实现多个接口,如果父类默认构造方法有参数需要传递合适的参数
open class A(x: Int) {
public open val y: Int = x
}
interface B {...}
val ab: A = object : A(1), B {
override val y = 12
}
有时候需要一个仅仅是一个对象的变量,不继承其他类,也可以直接用object
fun foo() {
val adHoc = object {
var x: Int = 0
var y: Int = 1
}
print(adHoc.x + adHoc.y)
}
需要注意的是,这种匿名的对象只能被用在局部或者private声明,如果将匿名对象作为public方法的返回值或者作为一个public的属性,那么【这个方法的返回值或属性的类型】的真实值被置为匿名对象的父类类型,如果没有继承就是Any,那么被声明在匿名对象内部的变量便不可访问。
class C {
//private方法,返回值是匿名对象的类型
private fun foo() = object {
val x: Int = 233
}
//public方法,返回值是 Any
fun publicFoo() = object {
val x: Int = 666
}
fun bar() {
val x1 = foo().x
val x2 = publicFoo().x //会发生错误,找不到这个变量
}
}
同Java一样object声明的匿名内部类可以访问局部变量,但是Java只能访问final修饰的,object则没有这个限制
fun countClicks(window: JCompenet) {
var clickCount = 0
var enterCount = 0
window.addMouseListener(object: MouseAdapter) {
override fun mouseClicked(e: Event) {
clickCount++
}
override fun mouseEntered(e: Event) {
enterCount++
}
}
}
2. object 声明
当用object 声明一个类时,表明这个类是一个单例类,这比在Java中声明一个单例类要简单得多
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
//...
}
val allDataProviders: Collection<DataProvider>
get() = //...
}
在使用时,可以直接使用类名作为对象来调用方法
DataProviderManaer.registerDataProvider(...)
这种单例类也可以继承其他类
object DefaultListener : MouseAdapter() {
override fun mouseClicked(e: Event) {...}
override fun mouseEntered(e: Event) {...}
}
注意object声明不能用在方法和inner内部类中,但是能嵌套在其他object声明和嵌套类中。
另外还需要注意:
object 定义后即刻实例化
因此 object 不能有定义构造函数
定义在类内部的 object 并不能访问类的成员
二. companion object
和 object 不同, companion object 的定义完全属于类的本身,所以 companion object 肯定是不能脱离类而定义在全局之中。它类似 Java 里的 static 变量,所以我们定义 companion object 变量的时候也一般会使用大写的命名方式。
和 object 类似,可以给 companion object 命名,也可以不给名字,这个时候它会有个默认的名字: Companion ,而且,它只在类里面能定义一次:
class MyClass {
companion object CompanionName {
val INNER_PARAMETER = "can only be inner"
fun newInstance() = MyClass("name")
}
}
class MyClass1 {
companion object {
val INNER_PARAMETER = "can only be inner"
}
}
fun main(vararg args:String) {
println(MyClass.CompanionName.INNER_PARAMETER == MyClass1.INNER_PARAMETER) //print: true
println(MyClass1.Companion.INNER_PARAMETER == MyClass1.INNER_PARAMETER) //print: true
}
扩展功能是Kotlin的一个强大的特性,一个类的companion object也可以进行扩展
class MyClass {
companion object {
val INNER_PARAM = "hhhhh"
}
fun main(varargs args: String) {
MyClass.Companion.foo() {
print(this.INNER_PARAM)
}
MyClass.foo()
}
}
虽然companion object中声明的变量类似于Java中的静态变量,但是在运行时他们仍然是一个真正实例的成员变量,所以companion可以实现一个接口
interface Factory<T> {
fun create(): T
}
class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}
可以使用 @JvmStatic 使Companion object 的成员真正成为静态成员。
ps:
object表达式是在使用的地方立即被初始化和执行的
object声明(一个类)是延迟加载的,只有当第一次被访问时才会初始化,所以被用来实现单例
companion object是当包含它的类被加载时就初始化了的,这一点和Java的static还是一样的
三. 总结
通过上面的一些介绍,对于object 和companion object 的用法有了一定的了解,其实官方并不建议我们到处滥用object 关键字,因为它不利于控制也不易于测试,毕竟它会在声明时就初始化。一般情况下我们使用的使用就是匿名内部类或者单例模式,而companion object一般用来声明静态域。