对象表达式(Object expression),对象声明,伴生对象(companion object)详解
对象表达式(Object expression)
Java当中匿名内部类在很多场景下大量使用。Kotlin的对象表达式就是为了解决匿名内部类的一些缺陷而产生的。
- 匿名内部类是没有名字的类
- 匿名内部类一定是继承了某个父类,或是实现了某个接口
- Java运行时将该匿名内部类当做它所实现的接口或是所继承的父类看待。
对象表达式的格式: Object[: 若干个父类型,中间用逗号分隔]
interface MyInterface{
fun myPrint(i:Int)
}
abstract class MyAbstractClass{
abstract val age:Int
abstract fun printMyAbstractInfo()
}
fun main(args: Array<String>) {
var myObject=object :MyInterface{
override fun myPrint(i: Int) {
println("i的值是$i")
}
}
myObject.myPrint(100)
println("-------------")
var myObject2=object {
init {
println("初始化完成")
}
var myProperty="Hello world"
fun myMethod()="myMethod()"
}
println(myObject2.myProperty)
println(myObject2.myMethod())
println("-------------")
var myObject3=object:MyInterface,MyAbstractClass() {
override fun myPrint(i: Int) {
println("i的值$i")
}
override val age: Int
get() = 30
override fun printMyAbstractInfo() {
println("printMyAbstractInfo")
}
}
myObject3.myPrint(50)
println(myObject3.age)
myObject3.printMyAbstractInfo()
}
注意:
匿名对象只能在局部变量范围内或是被private修饰的成员变量范围内才能被识别出真正的类型。如果将匿名对象当做一个public方法返回的类型或是public属性的类型,那么该方法或是属性的真正类型,就是该匿名对象所声明的父类型,如果没有声明任何父类型,那么其类型就是Any,在这种情况下匿名对象中声明的任何成员都是无法访问的。如下例子:
class MyClass {
var i:Int=0
private var myobject=object{ //此处的private是非常重要的,不能去掉否则访问不到output方法
fun output()= println("output invoked")
fun addNumber()=i++ //Kotlin对象表达式中的代码可以访问外层的变量,Java外层必须声明为final
}
fun myTest(){
println(myobject.javaClass)
myobject.output()
}
}
fun main(args: Array<String>) {
val myClass=MyClass()
myClass.myTest()
}
对象声明
并且它总是在Object关键字后跟一个名称,对象声明不是一个表达式,不能用在赋值语句的右边。对象声明的初始化过程是线程安全的。
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
// ……
}
val allDataProviders: Collection<DataProvider>
get() = // ……
}
伴生对象(companion object)
在kotlin中,与java不同的是,类是没有static方法的。在大多数情况下,Kotlin推荐的做法是使用包级别的函数作为静态方法,在Kotlin会将包级别的函数当做静态方法来提供。 伴生对象默认名字为Companion.
虽然伴生对象的成员看起来像java中的静态成员,但在运行期,他们依旧是真实对象的实例成员,在jvm上,可以将伴生对象的成员真正生成为类的静态方法与属性,这是通过@jvmStatic注解来实现的,伴生对象在编译后会生成一个静态内部类
class MyTest{
companion object{
var a:Int=20
@JvmStatic //@JvmStatic注解可以将伴生对象的成员真正生成为类的静态方法与属性
fun method(){
println("method invoked!")
}
fun method2(){
println("method2 invoked!")
}
}
}
fun main(args: Array<String>) {
MyTest.method()
println(MyTest.a)
MyTest.method2()
}
对象表达式和对象声明之间的区别
- 对象表达式是在使用他们的地方立即执行(及初始化)的;
- 对象声明是在第一次被访问到时延迟初始化的;
- 伴生对象的初始化是在相应的类被加载(解析)时,与 Java 静态初始化器的语义相匹配。