Edit Page

オブジェクト式と宣言

時にはあるクラスをわずかに修正しただけのオブジェクトをそれのための新しいサブクラスを明示的に宣言せずに、作成する必要があります。Javaでは 無名内部クラス でこの事例を処理します。 Kotlinではたった オブジェクト式オブジェクトの宣言 だけでこの概念を一般化します。

オブジェクト式

いくつかの型(1つでも複数でも)から継承する無名クラスのオブジェクトを作成するには、このようにします:

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
}

万が一、自明でないスーパータイプの「オブジェクトのみ」が必要な場合は、単純に次のように言うことができます:

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

ただ、Javaの無名内部クラスのように、内包するスコープからオブジェクト式のコードが変数にアクセスすることができます。(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++
    }
  })
  // ...
}

オブジェクトの宣言

シングルトンは非常に便利なパターンであり、Kotlin(Scalaの後です)は、シングルトンを容易に宣言で切るようにしました:

object DataProviderManager {
  fun registerDataProvider(provider: DataProvider) {
    // ...
  }

  val allDataProviders: Collection<DataProvider>
    get() = // ...
}
  • これは、オブジェクトの宣言と呼ばれ、それは常に object キーワードの後に名前を持ちます。ちょうど変数宣言と同じように、オブジェクトの宣言は式ではなく、代入文の右側に使用することはできません。

オブジェクトを参照するために、その名前を直接使用します。

DataProviderManager.registerDataProvider(...)

このようなオブジェクトは、スーパータイプを持つことができます:

object DefaultListener : MouseAdapter() {
  override fun mouseClicked(e: MouseEvent) {
    // ...
  }

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

:オブジェクト宣言はローカルにすることはできません(つまり、関数内で直接ネストしてください)。ただし、他のオブジェクト宣言または非内部クラスにネストすることもできます。

コンパニオンオブジェクト (Companion Objects)

クラス内のオブジェクトの宣言は、 companion キーワードでマークすることができます。

class MyClass {
  companion object Factory {
    fun create(): MyClass = MyClass()
  }
}

コンパニオンオブジェクトのメンバーは修飾子として単にクラス名を使用して呼び出すことができます:

val instance = MyClass.create()

コンパニオンオブジェクトの名前を省略することができます。この場合、 Companion という名前が使用されます。

class MyClass {
  companion object {
  }
}

val x = MyClass.Companion

コンパニオンオブジェクトのメンバは、他の言語のスタティックメンバのように見えますが、実行時にはそれらはまだ実際のオブジェクトのインスタンスメンバであり、たとえばインターフェイスを実装できます:

interface Factory<T> {
  fun create(): T
}


class MyClass {
  companion object : Factory<MyClass> {
    override fun create(): MyClass = MyClass()
  }
}

しかしながら、JVM上では、 @JvmStatic アノテーションを使用すると、コンパニオンオブジェクトのメンバを実際の静的メソッドやフィールドとして生成することができます。詳細については、Javaの相互運用性のセクションを参照してください。

オブジェクト式と宣言の間の意味の違い

オブジェクト式とオブジェクトの宣言の間には、ある重要な意味上の違いがあります:

  • オブジェクト式は すぐに 実行され(初期化され)、そこで使用されます
  • オブジェクト宣言は、初回アクセス時に 遅延して 初期化されます
  • コンパニオンオブジェクトは、対応するクラスが読み込まれた(解決)されたときに初期化され、これは Java の静的初期化子のセマンティクスに一致します