如何使用 Object 表達式以及宣告 Object

如何使用 Object 表達式以及宣告 Object

如果有時候你只是想要建立一個小型的物件,卻不想要宣告一個類別,Kotlin 允許你這樣做,這樣的寫法稱作 object expressionsobject declarations

說明

Object expressions

如果你要建立一個匿名類別實體,一般來說會這樣寫。

window.addMouseListener(object : MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) { /*...*/ }

    override fun mouseEntered(e: MouseEvent) { /*...*/ }
})

你可以同時繼承跟實作 class 以及 interface。

open class A(x: Int) {
    open val y: Int = x
}

interface B

val a1: A = object : A(1), B {}

val a2: A = object : A(3), B {
    override val y = 4
}

val b: B = object : A(3), B {
    override val y = 4
}

fun main(args: Array<String>) {
    println(a1.y)    //1
    println(a2.y)    //2
//    println(b.y)    //Unresolved reference 'y'
}

你也可以單純宣告一個物件。

fun foo() {
    val adHoc = object {
        var x: Int = 0
        var y: Int = 0
    }
    print(adHoc.x + adHoc.y)
}

這邊有幾個重點需要注意一下。

  • 如果是私有的方法後面接著 object,那麼回傳就會是一個匿名物件。

  • 如果是公開的方法後面接著 object,那麼回傳的就是 Any,如此一來透過這個方法存取匿名類別內的值就會無法存取。

class C {
    // Private function, so the return type is the anonymous object type
    private fun foo() = object {
        val x: String = "x"
    }

    // Public function, so the return type is Any
    fun publicFoo() = object {
        val x: String = "x"
    }

    fun bar() {
        val x1 = foo().x        // Works
        val x2 = publicFoo().x  // ERROR: Unresolved reference 'x'
    }
}

你也可以從外部存取變數並且改變它的值,這邊跟 Java 就不太一樣。

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 declarations

如果在 Kotlin 使用 Singleton,可以透過 object 關鍵字就可以達成,這邊有幾個重點要注意一下。

  • 不能有建構子。

  • 不能傳入參數,但是你可以透過方法來傳入。

  • 不用寫 class 關鍵字。

object Person{
    val userName = "givemepass"
}
  • 可以實作其他介面。
interface A{
    fun test()
}
object Person : A{
    override fun test() {
        TODO("not implemented")
    }
}

反組譯來看看就會長這樣。

public final class Person {
   @NotNull
   private static final String userName = "givemepass";
   public static final Person INSTANCE;

   @NotNull
   public final String getUserName() {
      return userName;
   }

   private Person() {
      INSTANCE = (Person)this;
      userName = "givemepass";
   }

   static {
      new Person();
   }
}

如果要存取就可以透過這樣的方式取得。

println(Person.userName)

結果如下。

givemepass

Companion Objects

使用 Companion object 關鍵字,你可以想像成 Java 的靜態變數。

class Person{
    val name = "givemepass"
    companion object {
        fun getInstance() = Person()
    }
}

透過以下方法就可以取得該類別物件,並且拿到成員變數。

fun main(args: Array<String>) {
    println(Person.getInstance().name)
}

結果如下。

givemepass

它也可以寫成以下三種狀態。

class MyClass1 {
    companion object {
        val num = 1
    }
}

class MyClass2 {
    companion object Named {
        val num = 2
    }
}

class MyClass3 {
    companion object {
        val num = 3
    }
}

fun main(args: Array<String>) {
    println(MyClass1.Companion.num)
    println(MyClass2.num)
    println(MyClass3.num)
}

結果如下。

1
2
3

object expressions、object declarations 與 companion object 的差異

  • object expressions 是立即性的,也就是說你馬上就可以得到一個物件。

  • object declarations 也就是 singleton,它是一種設計模式。

  • companion object 是 Java 的 static。

參考資料

https://kotlinlang.org/docs/reference/object-declarations.html