Kotlinのcompanion objectとは

普段から当たり前のように使ってはいたものの、あまりきちんと理解していなかった`companion object`についてざっと調べた内容をまとめました。

# `companion object`とは
`companion object`は**クラス内に作成されるSingleton**のことです。

# `companion object`の宣言方法
Kotlinでは、`class`の代わりに **`object`キーワードを使用するだけでSingletonが作成できます**。`object`キーワードはパッケージレベルから使用可能です。
詳しくは公式ドキュメントをご参照ください。

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

クラス内では、**`object`キーワードの前に`companion`修飾子を付与することでクラスに属するSingletonが作成できます**。これを`companion object`と呼びます。
**`companion object`は1クラス内に1つだけ宣言可能です**。また、オブジェクト名は省略可能です。
なお、`companion`修飾子を付与しない`object`は1クラス内に複数宣言可能です。

```kotlin:Hoge.kt
class Hoge {

  object A {
    val fizz = "fizz"
    fun foo() { ... }
  }

  companion object {
    val buzz = "buzz"
    fun bar() { ... }
  }

}
```

クラス内で作成される`companion object`と`object`に機能的な違いはないと思っています(間違っていたらご指摘ください)。

# `companion object`の使用方法
## Kotlinから使用するとき
`companion object`はクラスに属することが明白なので、Kotlin側から使用するときは以下のような簡潔な記述になります。

```kotlin:Main.kt
fun main() {
  // objectの場合。
  val fizz = Hoge.A.fizz
  Hoge.A.foo()

  // companion objectの場合。
  val buzz = Hoge.buzz
  Hoge.bar()
}
```

## Javaから使用するとき
**`companion object`内に宣言したプロパティ・メソッドは、Java側からは「Companion」というSingletonに属するフィールドのgetter/setter・メソッドとして見えます**。
また、`object`内に宣言したプロパティ・メソッドは、Java側からは「INSTANCE」というSingletonに属するフィールドのgetter/setter・メソッドとして見えます。

したがって、Java側から使用するときは以下のような記述になります。

```java:Main.java
public class Main {
  public static void main(String[] args) {
    // objectの場合。
    String fizz = Hoge.A.INSTANCE.getFizz();
    Hoge.A.INSTANCE.foo();

    // companion objectの場合。
    String buzz = Hoge.Companion.getBuzz();
    Hoge.Companion.bar();
  }
}
```

Android Studioでは、Kotlinプラグインをインストールした状態で`Tools` > `Kotlin` > `Show Kotlin Bytecode`を選択して表示されるペインで`Decompile`ボタンを押すことによって、デコンパイルされたコードを確認することができます。

```java:Hoge.decompiled.java
// @Metadataなどは省略しています。 
public final class Hoge {
  @NotNull
  // privateなので、Companionインスタンスのgetterを介してアクセス可能。
  private static final String buzz = "buzz";
  public static final Hoge.Companion Companion = new Hoge.Companion((DefaultConstructorMarker)null);

  public static final class A {
    @NotNull
    // privateなので、INSTANCEインスタンスのgetterを介してアクセス可能。
    private static final String fizz = "fizz";
    public static final Hoge.A INSTANCE;

    // Aクラス内に宣言されるので、INSTANCEインスタンスを介してアクセス可能。
    @NotNull
    public final String getFizz() {
      return fizz;
    }

    public final void foo() {
      ...
    }

    private A() {
    }

    static {
      Hoge.A var0 = new Hoge.A();
      INSTANCE = var0;
      fizz = "fizz";
    }
  }

  public static final class Companion {
    // Hogeクラス内に宣言されるので、Companionインスタンスを介してアクセス可能。
    @NotNull
    public final String getBuzz() {
      return Hoge.buzz;
    }

    public final void bar() {
      ...
    }

    private Companion() {
    }

    // $FF: synthetic method
    public Companion(DefaultConstructorMarker $constructor_marker) {
      this();
    }
  }
}
```

# Kotlinにstatic修飾子がない理由
**Kotlinにはstatic修飾子がない**ので、`companion object`はstaticなフィールドやメソッドが必要なときの代替手段として利用されることがほとんどだと思います。

2013年のものですが、「[“Static constants” in Kotlin](https://blog.jetbrains.com/kotlin/2013/06/static-constants-in-kotlin/)」という記事で[Andrey Breslavさん](https://twitter.com/abreslav)がKotlinにおけるstaticに対する考えについて言及していました。

> Kotlin is designed so that there’s no such thing as a “static member” in a class.
> If you have a function in a class, it can only be called on instances of this class.
> If you need something that is not attached to an instance of any class, you define it in a package, outside any class (we call it **package-level functions**):
> 
> But sometimes you need **static constants** in your class: for example, to comply with requirements of some framework or to use serialization.
How do you do this in Kotlin? There are two things in Kotlin that resemble Java’s statics: aforementioned **package-level functions** and **class objects**.

内容としては、

- Javaにおけるstaticな定数(`final`修飾子の付与されたフィールド)は、Kotlinではパッケージレベルに宣言するか`class object`を利用すればよい
- Javaにおけるstaticなメソッドは、Kotlinではパッケージレベルに宣言すればよい

このため、Kotlinにおいてstaticは不要であるといった意味だと理解しています。

上記で登場する`class object`は`companion object`のことを意味しています。
私はこの当時まだKotlinを使用したことがなかったので知りませんでしたが、M11で名称やコンセプトに関して変更があったことが[公式ブログ](https://blog.jetbrains.com/kotlin/2015/03/kotlin-m11-is-out/)に記載されていました。

# `companion object`のプロパティ・メソッドにJava側から直接アクセスする方法
## `@JvmField`/`@JvmStatic`アノテーション
`companion object`がJava側からどのように見えるかについて以下のように書きました。

> `companion object`内に宣言したプロパティ・メソッドは、Java側からは「Companion」というSingletonに属するフィールドのgetter/setter・メソッドとして見えます。

これらをJava側からstaticなフィールド・メソッドとして扱えるようにする方法が提供されています。それが`@JvmField`/`@JvmStatic`アノテーションです。
詳しくは公式ドキュメントをご参照ください。

https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html

使い方は以下のように`companion object`内のプロパティとメソッドに各アノテーションを付与するだけです。

```kotlin:Hoge.kt
class Hoge {

  companion object {
    @JvmField
    val buzz = "buzz"
    @JvmStatic
    fun bar() { ... }
  }

}
```

その結果、以下のような見え方になります。

```java:Hoge.decompiled.java
// @Metadataなどは省略しています。 
public final class Hoge {
  @JvmField
  @NotNull
  // Companionクラス外にpublicかつstaticで宣言されるので、直接アクセス可能。
  public static final String buzz = "buzz";
  public static final Hoge.Companion Companion = new Hoge.Companion((DefaultConstructorMarker)null);

  @JvmStatic
  // Companionクラス外にpublicかつstaticで宣言されるので、直接アクセス可能。
  public static final void bar() {
    Companion.bar();
  }

  public static final class Companion {
    @JvmStatic
    public final void bar() {
      ...
    }

    private Companion() {
    }

    // $FF: synthetic method
    public Companion(DefaultConstructorMarker $constructor_marker) {
      this();
    }
  }
}
```

したがって、Java側から使用するときもKotlin側から使用するときと同じように記述可能となります。

```java:Main.java
public class Main {
  public static void main(String[] args) {
    String buzz = Hoge.buzz;
    Hoge.bar();
  }
}
```