Kotlin でシングルトンを作成する

David Mbochi Njonge 2023年6月20日
  1. Kotlin API を使用してシングルトンを作成する
  2. コンパニオン オブジェクトを使用してシングルトンを作成する
  3. コンパニオン オブジェクトを使用してスレッド セーフなシングルトンを作成する
  4. Lazy Initializer を使用してシングルトンを作成する
  5. まとめ
Kotlin でシングルトンを作成する

設計パターンは、当面の要件に基づいて発生する一般的な問題に対する一連のソリューションであり、これらのソリューションは、同じ問題を解決しようとする他のシステムで再利用できます。 創造的、行動的、構造的なデザイン パターンなど、さまざまなデザイン パターンがあります。

作成設計パターンは、オブジェクトの作成方法を扱います。このチュートリアルでは、作成設計パターンの例であるシングルトン パターンの使用方法を学習します。

シングルトン パターンは、オブジェクトの単一のコピーを作成し、そのコピーへの 1つのアクセス ポイントを提供するのに役立つ設計パターンです。

アプリケーションを開発する場合、スレッド プール、ロギング、デバイス ドライバ、レジストリ設定、キャッシュなどのオブジェクトの単一コピーが必要になる場合があります。

これらのオブジェクトの複数のコピーを持つプログラムは、不適切な動作、リソースの過剰使用、一貫性のない結果につながる可能性があります。 このチュートリアルでは、Kotlin でシングルトンを作成するために使用できるさまざまな方法を学習します。

Kotlin API を使用してシングルトンを作成する

Kotlin は、オブジェクト作成コードを記述せずにすぐに使用できるシングルトンを提供します。 これを実現するには、object キーワードを使用してシングルトン名を定義する必要があり、このシングルトン内に任意のメンバー変数と関数を追加できます。

IntelliJ を開き、File > New > Project を選択します。 開いたウィンドウで、プロジェクト名をkotlin-singletonと入力し、[言語] セクションで [Kotlin] を選択し、[ビルド システム] セクションで [Intellij] を選択します。

最後に、Create ボタンを押してプロジェクトを生成します。

src/main/kotlin フォルダーの下に Main.kt という名前のファイルを作成し、次のコードをコピーしてファイルに貼り付けます。

object Singleton{
    fun showMessage(): Singleton {
        return Singleton
    }
}

fun main(){
    println(Singleton.showMessage())
    println(Singleton.showMessage())
}

前述のように、これは Kotlin でシングルトンを作成するデフォルトの方法であり、実装は非常に簡単です。 作成されたシングルトン オブジェクトはスレッド セーフであることに注意してください。つまり、マルチスレッド プログラムでも新しいオブジェクトを作成できないため、データの不整合が生じる可能性があります。

このコードでは、Singleton という名前のシングルトンと showMessage() という名前のメンバー関数を作成しました。 この関数は、作成されたシングルトンへの参照を返します。

このコードを実行すると、以下に示すように、メンバー関数への 2つの呼び出しが同じオブジェクトへの参照を返すことに注意してください。

Singleton@5305068a
Singleton@5305068a

コンパニオン オブジェクトを使用してシングルトンを作成する

前の例をコメントアウトし、次のコードをコピーして Main.kt ファイルのコメントの後に貼り付けます。

class Singleton private constructor(){
    companion object{
        var singleton = Singleton();
        fun getInstance(): Singleton{
            if (singleton == null){
                singleton = Singleton();
            }
            return singleton;
        }
    }
}


fun main(){
    println(Singleton.getInstance());
    println(Singleton.getInstance());
}

前の例とこの例の唯一の違いは、コンパニオン オブジェクト がクラス内で使用されていることです。 Kotlin のすべての静的変数と関数は、コンパニオン オブジェクト内に配置されます。

このコードでは、オブジェクトのコピーが 1つだけ作成されるようにする getInstance() という名前のメソッドを作成しました。 これを確実に守るために、constructor() を非公開にし、オブジェクトを作成する前にオブジェクトが存在するかどうかを確認します。

このアプローチはうまく機能しますが、スレッドセーフではないことに注意してください。 アプリケーションがマルチスレッド化されている場合、getInstance() メソッドに同時にアクセスでき、前述の問題につながる可能性のある複数のオブジェクトにつながります。

次のセクションでは、シングルトンをスレッドセーフにする方法を示します。

このコードを実行し、出力が以下に示す同じオブジェクトへの参照を返すことを確認します。

Singleton@1f32e575
Singleton@1f32e575

コンパニオン オブジェクトを使用してスレッド セーフなシングルトンを作成する

前のコードをコメントアウトし、次のコードをコピーして Main.kt ファイルのコメントの後に貼り付けます。

class Singleton private constructor(){
    companion object{

        private var singleton = Singleton();

        @Synchronized
        fun getInstance(): Singleton{
            if (singleton == null){
                singleton = Singleton();
            }
            return singleton
        }
    }
}

このコードは前のコードと似ていることに注意してください。 唯一の違いは、getInstance() メソッドに @Synchronized アノテーションを追加したことです。

このアプローチは、Java では同期メソッドと呼ばれます。 スレッドセーフはロックによって実現されます。

通常、オブジェクトにはロックがあり、スレッドが同期メソッドに到達すると、オブジェクトの作成が完了するまでこのロックを排他的に取得します。つまり、他のスレッドはこのメソッドにアクセスできません。

別のスレッドがロックにアクセスすると、既にオブジェクトがあり、スレッドは新しいスレッドを作成せずに既存のスレッドを使用します。 メソッド全体ではなくコードの一部を同期するのに役立つ同期ブロックを使用するなど、同期を実現する方法は他にもあります。

同期によって問題は解決されますが、アプリケーションにパフォーマンスの問題が発生することに注意してください。 同期は、アプリケーションのパフォーマンスを100分の 1 に低下させます。アプリケーションのパフォーマンスが重要な場合は、同期の使用を再検討する必要があります。

遅延初期化から積極的初期化、ロックのダブルチェック、またはパフォーマンスがアプリケーションに影響を与えない場合はそのままにしておくアプローチを使用できます。

Lazy Initializer を使用してシングルトンを作成する

前のコードをコメントアウトし、次のコードをコピーして Main.kt ファイルのコメントの後に貼り付けます。

class Singleton{
    companion object{
        val singleton: Singleton by lazy {
            Singleton();
        }
    }

    fun showMessage(): Singleton {
        return Singleton.singleton
    }
}

fun main(){
    println(Singleton.singleton.showMessage())
    println(Singleton.singleton.showMessage())
}

このコードでは、渡した引数を使用してスレッドセーフな Lazy のインスタンスを作成する関数 lazy() を使用してシングルトンを作成しました。 LazyThreadSafetyMode.SYNCHRONIZED を使用して、遅延インスタンスの初期化に 1つのロックのみが使用されるようにします。

このコードを実行し、出力が以下に示す同じオブジェクトへの参照を返すことを確認します。

Singleton@3e3abc88
Singleton@3e3abc88

まとめ

このチュートリアルでは、Kotlin でシングルトン オブジェクトを作成するために使用できるさまざまな方法を学びました。 対象となるアプローチには、object キーワード、companion object、および lazy initializer 関数の使用が含まれます。

同期メソッドを使用して、コンパニオン オブジェクト を使用するときにシングルトン オブジェクトがスレッド セーフであることを確認する方法も学びました。

David Mbochi Njonge avatar David Mbochi Njonge avatar

David is a back end developer with a major in computer science. He loves to solve problems using technology, learning new things, and making new friends. David is currently a technical writer who enjoys making hard concepts easier for other developers to understand and his work has been published on multiple sites.

LinkedIn GitHub