对比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 关键字的主要内容,如果未能解决你的问题,请参考以下文章

对比Java学Kotlinobject 关键字

对比Java学Kotlinobject 关键字

Kotlinobject 对象 ( object 关键字 | 对象声明 | 对象表达式 | 伴生对象 )

Kotlinobject 对象 ( object 关键字 | 对象声明 | 对象表达式 | 伴生对象 )

对比Java学Kotlin枚举

对比Java学Kotlin枚举