KotlinのCompanion object: とは何か、構文と使用方法

著者: IT Sectr 公開日: 2026-06-21 読了時間: 9 分

companion objectは、Javaのstaticキーワードに代わってクラスの静的メンバーを宣言するKotlin言語のメカニズムです。Kotlinには組み込みのstaticがありません — 代わりに、クラス内でcompanion修飾子を付けたobject宣言を使用します。JetBrains, 2025によると、companion objectを使用するとクラス名を通じてメソッドとプロパティを呼び出すことができ、Javaエコシステムにおける静的メンバーの完全な等価物となります。

重要なポイント

  • companion objectは、companionキーワードを付けたクラス内のobject宣言であり、Javaのstaticを置き換える
  • 静的メンバーはインスタンスを作成せずにクラス名を通じてアクセス可能: ClassName.method()
  • 名前付きcompanion objectは、より読みやすいコードのためにオブジェクトに名前を付けられる
  • インターフェースもKotlinではメソッド実装を持つcompanion objectを含めることができる
  • @JvmStatic — companion objectのメソッドをJavaで実際のstaticメソッドとしてエクスポートするアノテーション

Kotlinのcompanion objectとは?

companion objectは、companionキーワードでマークされたKotlinのobject宣言の特別な種類です。これにより、インスタンスではなくクラスに属するメンバー — つまりJava用語での静的メンバーを宣言できます。staticが個々のフィールドとメソッドの修飾子であるJavaとは異なり、Kotlinでは静的メンバーは単一のコンパニオンオブジェクト内にグループ化されます。

Kotlinの任意のクラスは正確に1つのcompanion objectを含めることができます。このオブジェクトのメンバーは、インスタンスを作成せずにクラス名を通じてアクセス可能です: MyClass.method()。バイトコードの観点では、companion objectは別の内部クラスにコンパイルされ、@JvmStaticアノテーションを使用するとそのメソッドは外部クラスの静的メソッドになります。

Google I/O 2024によると、companion objectはKotlinで書かれた最新のAndroidアプリケーションにおけるファクトリメソッド、定数、ユーティリティ関数の主要なパターンになっています。開発者はパッケージレベル関数を持つユーティリティクラスよりもこれを選択します。companion objectは所有クラスとの論理的な接続を維持するからです。

staticコンテキストをグループ化するためにcompanion objectを使用してください — 論理的にはクラスに属するがインスタンスを必要としない定数、ファクトリ、ヘルパーメソッドなど。

Companion objectの構文: 宣言とアクセス

companion objectの基本構文はシンプルです: companionキーワードをクラス内のobject宣言の前に置きます。名前が指定されない場合、オブジェクトはCompanionという名前になり、明示的または暗黙的にアクセスできます。

kotlin
class User {
    companion object {
        const val TABLE_NAME = "users"

        fun create(name: String): User {
            return User(name)
        }
    }

    val name: String
    constructor(name: String) { this.name = name }
}

// Access via class name
val table = User.TABLE_NAME
val user = User.create("Alice")

上記の例では、TABLE_NAMEとcreate()はUser.TABLE_NAMEとUser.create()を通じてアクセス可能です。プリミティブ定数のconst修飾子に注意してください — これはコンパイル時の値のインライン化を保証し、プリミティブとStringに対するJavaのstatic finalと同様です。

Companionを通じた暗黙的および明示的アクセス

companion objectに名前がない場合、自動名Companion: User.Companion.TABLE_NAMEを通じてアクセスできます。実際にはこれはほとんど必要ありませんが、Javaコードから呼び出す場合やリフレクションを使用する場合に役立ちます。

kotlin
// Both calls are equivalent
User.create("Bob")
User.Companion.create("Bob")

// Getting reference to companion object
val companion: User.Companion = User

名前付きcompanion objectと特徴

companion objectには名前を付けることができ、コードの可読性が向上し、意味のある名前でアクセスできるようになります。ファクトリとして使用されるcompanion objectではFactoryが最も一般的な名前です。

kotlin
class HttpResponse {
    companion object Factory {
        fun ok(body: String): HttpResponse = HttpResponse(200, body)
        fun notFound(): HttpResponse = HttpResponse(404, "Not Found")
        fun serverError(): HttpResponse = HttpResponse(500, "Internal Error")
    }
}

// Access via Factory name
val response = HttpResponse.Factory.ok("data")

名前付きcompanion objectは、静的メンバーの目的を文書化するのに役立ちます。リファクタリングにおいて、名前はコードを自己文書化します — 開発者はFactoryがHttpResponseのインスタンスを作成することを即座に理解できます。

制限: クラスあたり1つのcompanion objectのみ

クラスは1つのcompanion objectのみを含めることができます。これは、グループ化なしで任意の数の静的フィールドとメソッドを宣言できるJavaとの基本的な違いです。静的メンバーの複数の論理グループが必要な場合は、companionなしのネストされたobject宣言を使用してください。

公式Kotlinドキュメント(Kotlin Docs, 2025)によると、1つのcompanion objectの制限は言語設計に動機づけられています: 静的メンバーはクラスと緊密に結合されるべきであり、1つのコンパニオンオブジェクトがこの接続の明確な境界を提供します。静的メンバーが多すぎる場合は、クラスのリファクタリングのシグナルです。

インターフェース内のCompanion object

Kotlinでは、インターフェースもcompanion objectを含めることができます。これにより、Javaでは不可能な、インターフェース内に直接静的メソッドと定数を定義できます。このアプローチは、インターフェースに関連する定数やファクトリメソッドの宣言によく使用されます。

kotlin
interface ApiService {
    companion object {
        const val BASE_URL = "https://api.example.com"
        const val TIMEOUT_MS = 5000

        fun create(client: OkHttpClient): ApiService =
            Retrofit.Builder()
                .baseUrl(BASE_URL)
                .client(client)
                .build()
                .create(ApiService::class.java)
    }
}

この例では、BASE_URL、TIMEOUT_MS、create()はApiServiceインターフェースに属し、その実装には属しません。これは便利です: APIサービスインスタンスを作成するためのすべての定数とファクトリロジックが1つの場所 — インターフェース自体の内部にあります。

インターフェースの実装はcompanion objectのメンバーを継承しません — これらはインターフェース名: ApiService.BASE_URLを通じてのみ呼び出されます。

@JvmStaticと@JvmField: Java互換性

Javaコードからcompanion objectのメソッドを呼び出す場合、デフォルトでは外部クラスの静的メソッドとしてではなく、ネストされたCompanionクラスのメソッドとしてアクセス可能です。それらをJavaの実際の静的メソッドとフィールドとしてエクスポートするには、メソッドに@JvmStaticアノテーションを、フィールドに@JvmFieldアノテーションを使用します。

アノテーション適用Javaでの結果
@JvmStaticCompanion objectのメソッド静的メソッド: ClassName.method()
@JvmFieldCompanion objectのフィールド静的フィールド: ClassName.field
constプリミティブとStringインライン定数: コンパイラが値を置換
アノテーションなしデフォルトのメソッド/フィールドCompanion.method() / Companion.field
kotlin
class MathUtils {
    companion object {
        const val PI = 3.14159

        @JvmStatic
        fun square(x: Int): Int = x * x

        @JvmField
        val TAG: String = "MathUtils"
    }
}

// Called in Java as MathUtils.square(5), MathUtils.TAG

Javaコードからアクセス可能なすべてのパブリックなcompanion objectメソッドには@JvmStaticを使用することをお勧めします。constはプリミティブ型とStringにのみ適用されます — 他の型には@JvmFieldを使用してください。

Companion objectの実用的な使用例

実際のプロジェクトでは、companion objectはいくつかの標準的なシナリオで使用されます。ファクトリメソッドが最も一般的なパターンです: 異なるシグネチャを持つ複数のコンストラクタの代わりに、companion objectで名前付きファクトリメソッドを使用します。

kotlin
sealed class NetworkResult<out T> {
    data class Success<out T>(val data: T) : NetworkResult<T>()
    data class Error(val message: String) : NetworkResult<Nothing>()

    companion object {
        fun loading<T>(): NetworkResult<T> =
            Loading()
    }
}

private class Loading<T> : NetworkResult<T>()

NetworkResultのcompanion objectは、正しいジェネリック型でLoadingインスタンスを作成するファクトリメソッドloading()を提供します。このメソッドがないと、Loading()を直接使用してインスタンスを作成する必要があり、内部実装が公開されてしまいます。

定数とロギングタグ

companion objectは、クラス固有の定数を格納するためによく使用されます。const valで宣言された定数はコンパイル時にインライン化され、実行時のオーバーヘッドがゼロになります。

kotlin
class UserRepository {
    companion object {
        private const val TAG = "UserRepository"
        private const val CACHE_SIZE = 100
        private const val DEFAULT_PAGE_SIZE = 20
    }
}

よくある質問

Kotlinの通常のobjectとcompanion objectの違いは何ですか?

通常のobjectは、クラスから独立して存在する完全なシングルトンです。companion objectはクラス内のobjectであり、そのメンバーは外部クラス名を通じて呼び出されます。バイトコードでは、companion objectは静的ネストクラスになり、通常のobjectは自律的なシングルトンクラスになります。

companion objectは継承できますか?

いいえ、companion objectはサブクラスに継承されません。クラスBがクラスAを拡張する場合、B.method()はクラスAのcompanion objectのメソッドを呼び出しません — A.method()を通じて呼び出す必要があります。これはJavaの静的メソッドの動作と一致します。

Javaコードからcompanion objectを呼び出すには?

デフォルトでは — ClassName.Companion.method()を通じて。静的メソッドとして呼び出すには、companion objectのメソッドに@JvmStaticを追加します。フィールドには@JvmFieldを使用するか、プリミティブの場合はconst valで宣言します。

なぜKotlinにはstaticキーワードがないのですか?

Kotlinの開発者は、オブジェクトとの統一的な作業のためにstaticを廃止しcompanion objectを採用することを決定しました。Javaのstaticは、静的メソッドがインスタンスに結びついていないため、OOPの原則に違反します。companion objectは第一級オブジェクトであり、受け渡し、拡張関数による拡張、インターフェースの実装が可能です。

companion objectはパフォーマンスに影響しますか?

プリミティブとStringにconst valを使用する場合 — オーバーヘッドはゼロで、値はバイトコードにインライン化されます。メソッドの場合、メソッドがinlineでない限りオーバーヘッドはありません。ほとんどの場合、companion objectはパフォーマンスに測定可能な影響を与えません。@JvmStaticはJavaから呼び出されるパブリックAPIにのみ使用してください。

まとめ

  • companion objectは、Javaのstaticを置き換えるKotlinのクラス静的メンバー宣言メカニズム
  • 1つのcompanion objectがクラスあたり — すべての静的要素を1つのコンパニオンオブジェクトにグループ化
  • アクセスはクラス名を通じて: ClassName.method() または ClassName.Companion.method()
  • @JvmStaticと@JvmFieldがメンバーをJavaの実際の静的メソッドとフィールドとしてエクスポート
  • const valはプリミティブとString用 — オーバーヘッドゼロでコンパイル時に値をインライン化
  • インターフェースも定数とファクトリメソッドのためにcompanion objectをサポート
  • 名前付きcompanion objectがコードの可読性を向上させ、静的メンバーの目的を自己文書化

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください