对比Java学Kotlinobject 关键字
Posted 陈蒙_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了对比Java学Kotlinobject 关键字相关的知识,希望对你有一定的参考价值。
Java:啥?Object?这个我熟呀,我的祖先类……
Kotlin:打住,此 Object 非彼 Object,我的 Object 是个关键字,而不是某个具体的类,你说的 Object 相当于我的 Any
Java:额(⊙o⊙)…为啥要有 object 关键字?
Kotlin:铁子,问到点子上了,当你想稍微修改某个现有的类,但是呢你又不想通过继承或组合的方式新建一个类,这是你可以用 object 修饰表达式来实现。
Kotlin:还有就是我没有 static 关键字,但是很多场景下又需要跟类相关而跟实例无关的功能,这是可以用 object 来完成。
Java:说的有点抽象
Kotlin:下面详细说下
两种用法
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()
这时 Kotlin 同学举手示意要发言:这个我也行呀,你看:
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 关键字
可以在类里面用 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 关键字 | 对象声明 | 对象表达式 | 伴生对象 )