区别Kotlin中的object和companion object关键字
Posted Kotlin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了区别Kotlin中的object和companion object关键字相关的知识,希望对你有一定的参考价值。
一、前言
我们经常能够在 Java 相关技术博客中看到类似这样的文章: Java 中 X 种单例模式的写法。的确,单例模式是一个简单但又很重要的设计模式,特别是在多线程编程中,它的实现方法各有不同,也是适应各种不同的场合。不过,现在有了 Kotlin ,那都不是事了,忘记那个 X 吧,一个 object
关键字轻松搞定!真的,相信我,生活就是要那么容易。 :sunglasses:
在 Kotlin 中,除了 object
关键字还有个 companionobject
关键字,这个又是什么鬼?怎么使用?有啥区别?在没有仔细阅读相关文档资料之前还真有点傻傻分不清了。实践出真知,在经过简单的练习加上相关博客文章、源码阅读、谷歌搜索后,我心里所认识的 object
和 companionobject
是这样的:
object 可以定义在全局也可以在类的内部使用
object 就是单例模式的化身
object 可以实现 Java 中的匿名类
companion object 就是 Java 中的 static 变量
companion object 只能定义在对应的类中
但是,这些认识都是停留在表面上,在我继续阅读《 Kotlin in Action 》这本书相应章节后,我能发现它们的使用场景和功能点远不止这些!究其原因,主要是我并没有完全弄清楚它们的原理以及它们之间的差别,不论是 object
还是 companionobject
,它们的共性和区别还有这些:
object 可以作为变量的定义也可以是表达式
object 匿名类可以继承并超越 Java 中匿名类而实现多个接口
object 表达式当场实例化,但定义的 object 变量是延迟实例化的
object 和 companion object 都可以为其取名也可以隐姓埋名
object 匿名内部类甚至可以引用并更改局部变量
companion object 甚至还可以被扩展
Java 中需要结合 @JvmStatic 和 @JvmField 使用
…… 还有很多异同点等着你的开发
既然这俩兄弟有这么多异同点,那么我觉得非常有必要总结一下,以便将来能够更加得心应手地使用 Kotlin 吧。
二、正文
1. object基本定义
object
可以轻松实现 Kotlin 单例模式, 它可以定义在全局之中,也可以定义在类的内部。但是要注意几点:
object 定义后即刻实例化
因此 object 不能有定义构造函数
定义在类内部的 object 并不能访问类的成员
object OutObject {
val outParameter = "out"
fun outFunction(parameter:String) {
println("Out object function result: $parameter.")
}
}
class MyClass {
val classParameter = "class"
object InnerObject {
//val innerParameter = this.classParameter //error: 1,this refers to InnerObject 2,classParameter cannot be reached
val innerParameter = "inner"
fun innerFunction(parameter:String) {
println("Out object function result: $parameter.")
}
}
}
2. object作为表达式
在 android 开发中,我们经常会设置一个接口匿名类作为点击事件的参数: setOnClickListener(View.OnClickListener)
,这个时候在 Kotlin 中就可以使用 object
来表达那些匿名内部类。同时 object
相比 Java 更加强大,在用其表达内部类的时候有这几个注意点:
object 继承父类必须立刻传递父类构造参数
object 匿名类可以同时实现多个接口
object 匿名类作为参数并没有名字定义,但是可以为其定义一个变量名,如果实现多接口不能直接用类型推断,拗口吧,请看下面代码:
interface MyInterface1
interface MyInterface2
open class MySuperClass(parameter:String)
//button.setOnClickListener( object:OnClickListener { //... } ) //no name specified
class MyClass {
object AnonymousSubClass:MySuperClass("something"), MyInterface1, MyInterface2{
//do something...
}
val anonymousClass = AnonymousClass
object AnonymousClass:MyInterface1, MyInterface2 {
//do something...
}
val anotherAnonymous = object:MyInterface1 {
//type inferred
}
val againAnonymous:MyInterface1 = object:MyInterface1, MyInterface2 {
//type cannot be inferred
}
}
3. object可以访问非final局部变量
我们知道在 Java 中,内部类是不可以访问外部的非 final
成员变量的,也就是说:它不允许更改变量值!但是, Kotlin 的 object
可以。看代码:
interface MyInterface { fun operateVariable() }
class MyClass {
fun operationWithInterface(anonymous:MyInterface) { anonymous.operateVariable() }
init {
var localVariable = 1
this.operationWithInterface(object : MyInterface, MyInterface1 {
override fun operateVariable() {
localVariable += 1
}
})
println("Local variable value: $localVariable") //print: Local variable value: 2
}
}
就是那么霸道!写了那么多 object
,我们再看看 companionobject
,可谓是 object
的孪生兄弟,它可以说是为 Java 里的 static
而生的 object
。
4. companion object使用方法
和 object
不同, companionobject
的定义完全属于类的本身,所以 companionobject
肯定是不能脱离类而定义在全局之中。它就像 Java 里的 static
变量,所以我们定义 companionobject
变量的时候也一般会使用大写的命名方式。
同时,和 object
类似,可以给 companionobject
命名,也可以不给名字,这个时候它会有个默认的名字: Companion
,而且,它只在类里面能定义一次:
class MyClass2 {
companion object CompanionName {
val INNER_PARAMETER = "can only be inner"
fun newInstance() = MyClass2("name")
}
}
class MyClass3 {
companion object {
val INNER_PARAMETER = "can only be inner"
}
}
fun main(vararg args:String) {
println(MyClass2.CompanionName.INNER_PARAMETER == MyClass2.INNER_PARAMETER) //print: true
println(MyClass3.Companion.INNER_PARAMETER == MyClass3.INNER_PARAMETER) //print: true
}
5. 类名可作为接口参数传入
和 object
还是一样, companionobject
也可以实现接口,因为 companionobject
寄生于类,甚至类还可以直接作为实现了相应得接口的参数形式传入,拗口,看代码:
interface MyInterface { fun operateVariable() }
fun operateClass(interfaceObject:MyInterface) = interfaceObject.operateVariable()
class MyClass3 {
companion object:MyInterface {
override fun operateVariable() {
//do something...
}
}
}
fun main(vararg args:String) {
operateClass(MyClass3) //MyClass3 is now as the instance of MyInterface
}
6. 扩展类的静态成员
Kotlin 的扩展功能非常强大,是程序猿爱不释口且口口相传的实用特性之一。那么我们怎么扩展类的静态成员呢?这个时候当然是 companionobject
派上用场的时刻了!
class MyClass2 {
companion object {
val INNER_PARAMETER = "can only be inner"
}
}
fun main(vararg args:String) {
fun MyClass2.Companion.operateVariable() {
println(this.INNER_PARAMETER)
}
MyClass2.operateVariable() //print: can only be inner
}
怎么样? Kolint 就是那么强大!不得不服! :sunglasses:
三、总结
以上就是我自己总结的一些基本点,总之, Kolint 真不愧是一个门好语言啊!另外官方并不建议我们到处滥用 object
关键字,因为它不易控制也不利于测试,毕竟定义即实例化嘛,当然除了很容易实现单例模式,工厂模式也很简单,不信你可以试试。 :wink:
话又说回来,我建议大家有时间还是有必要再把 Kotlin 代码转换成 Java 源码再分析一遍,这个时候 @JvmStatic
和 @JvmField
标志就发挥作用了。我写这篇文章的时候我并没有下功夫继续深究,有机会还会再去看看转化 Java 部分源码,那样会更加加深对 object
和 companionobject
甚至整个 Kotlin 语言的认识吧!好吧,我就菜鸟一枚,那接下来就交给你总结一下并发表给我学习学习吧!谢谢! :grin:
最后,引用官方文档说明,比较它们的实例化过程:
object expressions are executed (and initialized) immediately, where they are used
object declarations are initialized lazily, when accessed for the first time
a companion object is initialized when the corresponding class is loaded (resolved), matching the semantics of a Java static initializer
资料: Kotlin笔记 Object表达式和声明: http://www.jianshu.com/p/f316ff2f4306Object Expressions and Declarations: https://kotlinlang.org/docs/reference/object-declarations.html
以上是关于区别Kotlin中的object和companion object关键字的主要内容,如果未能解决你的问题,请参考以下文章
kotlin用object实现单例模式,companion object与java静态
kotlin用object实现单例模式,companion object与java静态