对比Java学Kotlinobject 关键字
Posted 陈蒙_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了对比Java学Kotlinobject 关键字相关的知识,希望对你有一定的参考价值。
两种用法
Kotlin 的 object 关键字有两种用法,一个是作为右值表达式的前缀,一个是作为类的前缀修饰符。
object 表达式
object 表达式一般用于对现有类进行稍微修改、因为是临时使用一次而不值得新建类完成的场景,我们称其为匿名对象。
Any 类型的匿名对象
先来直观感受下用法:
val o = object
val hello = "Hello"
val world = "World"
override fun toString(): String
return "$hello, $world"
val r = object : Runnable
override fun run()
TODO("Not yet implemented")
r.run()
这时 Java 同学举手示意要发言:这个我也行呀,你看:
Object o = new Object()
String hello = "Hello";
String world = "World";
@Override
public String toString()
return hello + ", " + world;
;
// 还有这种匿名对象:
Runnable r = new Runnable()
@Override
public void run()
;
r.run();
看起来确实挺像的,但是,在 Kotlin 中我们可以随意加其他方法,并能引用到这些方法:
val o = object
val hello = "Hello"
val world = "World"
override fun toString(): String
return "$hello, $world"
fun length(): Int
return toString().length
val l = o.length() // 注意此行代码,在 kotlin 中可以正确运行
Java:矮油,有点吊哦,我是不允许这种调用的,会直接编译报错:
Object o = new Object()
String hello = "Hello";
String world = "World";
@Override
public String toString()
return hello + ", " + world;
public int length()
return toString().length();
;
int l = o.length(); // 编译报错:Cannot resolve method 'length' in 'Object'
具体类型的匿名对象
可以看出上述 object 表达式是祖先类 Any 的匿名对象。我们不仅可以对 Any,还能对某个具体的类/接口定义匿名对象,具体用法是 object 关键字+冒号:
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
匿名对象做返回值
匿名对象类型除了以普通变量的形式出现,还可以当做函数的返回值:
class C
private fun getObject() = object // private 方法,所以 printX() 方法可以引用成员变量 x
val x: String = "x"
fun printX()
println(getObject().x) // 可以引用到 x
但是并不是所有场景下都可以引用匿名对象的成员的,如下场景是可以的:
- 当匿名对象是局部变量时;
- 作为成员函数返回值或成员变量出现时,成员需是 private 且不是 inline 的;
当成员不是 private 或者是 inline是,我们无法引用到匿名对象的成员,只会把它当做未进行过修改的类型进行解析,这个类型由成员显式声明的类型决定:
interface A
fun funFromA()
interface B
class C
// public成员方法,返回类型为 Any,无法引用到 x
fun getObject() = object
val x: String = "x"
// public成员方法,返回类型为 A,无法引用到 x
fun getObjectA() = object: A
override fun funFromA()
val x: String = "x"
// public成员方法,返回类型是显式声明的类型 B,无法引用到方法 funFromA() 和 x
// 而且这种同时存在多个基类的场景必须要显式声明返回类型 B,否则会报错 Right-hand side has anonymous type. Please specify type explicitly
fun getObjectB(): B = object: A, B
override fun funFromA()
val x: String = "x"
闭包
与 Java 中的匿名类一样,Kotlin 的匿名对象也可以引用到匿名对象外部的变量,从而实现闭包的效果,而且不需要像 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++
)
// ...
object 声明
object 关键字另一种用法是修饰类或者类的成员,但是不能修饰局部变量。其中最常见的是直接修饰类,这时被修饰的类就是个线程安全的单例:
object DataProviderManager
fun registerDataProvider(provider: DataProvider)
// ...
val allDataProviders: Collection<DataProvider>
get() = // ...
在 Kotlin 中使用上述单例:
DataProviderManager.registerDataProvider(...)
在 Java 中使用上述单例:
DataProviderManager.INSTANCE.registerDataProvider(...)
companion 关键字
当我们想像 Java static 关键字那样,不借助某个具体实例来引用类的成员方法或属性时,可以在类里面用 companion object:
interface Factory<T>
fun create(): T
class MyClass
companion object Company : Factory<MyClass>
override fun create(): MyClass = MyClass()
val f: Factory<MyClass> = MyClass
f.create()
val f1: Factory<MyClass> = MyClass.Company
f1.create()
需要注意的是:
- 类里面最多只能有一个 companion object;
- 上述例子中的自定义名称 Company 可以省略,不影响使用,如果省略 Company 则调用的时候可以使用 MyClass.Companion,或者直接使用 MyClass 均可,MyClass 就代指其内部的 companion object;
- 外部类的成员可以引用 companion object 里面的成员;
- companion object 的用法看起来跟 Java 的 static 关键字有点像,但是其本质上还是一个对象,上述示例中 Company 实现了 Factory 接口也印证了这一点,如果要用真正的 static 字段,需要使用 @JVMStatic 注解,且 @JVMStatic 注解只能用于 object 或 companion object 修饰的对象的成员;
表达式和声明的区别
- 表达式在声明的地方就会被立即初始化和执行;
- 声明是延迟初始化的,即第一次被使用的时候才会初始化;
以上是关于对比Java学Kotlinobject 关键字的主要内容,如果未能解决你的问题,请参考以下文章
Kotlinobject 对象 ( object 关键字 | 对象声明 | 对象表达式 | 伴生对象 )