【正社員】還元率83%【フリーランス】マージン一律5万円で案件をご紹介させていただきます。 詳細はこちら

【Android】<Kotlin>シングルトンを理解する!Companion objectとの違いについても徹底解説

【Android】<Kotlin>シングルトンを理解する!Companion objectとの違いについても徹底解説
すだ

みなさまこんにちは〜!
メモリアインクのすだです。

今回は、Kotlinでの開発におけるシングルトンの使い方や、companion objectとの違いについてわかりやすく解説していきます!

この記事を読んでわかること…
・シングルトンとは?
・Kotlinでの開発におけるシングルトンの使い方
・companion objectとは?シングルトンとの違いについて

目次

環境

  • Kotlin (ver 1.9.0)
  • Android Studio (Giraffe | 2022.3.1 Patch 3)

シングルトンとは?

シングルトン(Singleton)とは、プログラム内で「インスタンスを1つだけしか持たないようにする」デザインパターンのことです。

つまり、

  • 1回だけ初期化されて
  • 以後はずっと同じ1個を使い続ける

という特徴を持っています。
(これは Kotlinに限らず、オブジェクト指向言語での開発には共通の知識となります。)

プログラムを書いていく中で、「どこでも使いたいけど、何回も作る必要がないもの」がいくつもあると思います。
たとえば、
・アプリ設定(AppConfig)… 全体で共通の状態を保持したい
・ログ管理(Logger)… すべてのログを1か所に集約したい
・データベース接続 … 接続を共有してリソース節約したい
・ユーザーセッション … 認証状態は全画面で共通であるべき
→こういったケースでは、インスタンスをたくさん作ると非効率 or バグの元になります。

そんな中で、シングルトンパターンを用いれば、毎回インスタンスを生成する必要がなく、
「アプリ全体で1つだけの共通機能」として組み込んでいくことができます。

Kotlinにおけるシングルトンの実装方法

基本的な書き方

Kotlinでは、object を使うだけでシングルトンが作れます。
最初に1回だけインスタンスが作られ、それ以降はずっとそれを使い回すようになります。

object AppLogger {
    var logCount = 0

    fun log(msg: String) {
        logCount++
        println("[$logCount] $msg")
    }
}

どこで呼んでも、この AppLogger は常に同じインスタンス(=同じlogCountを共有)です。

AppLogger.log("A")  // → [1] A
AppLogger.log("B")  // → [2] B

(↑logCount が増えてるのは、インスタンスが1つだから)

より実践的な使い方

実践的な使い方を練習するために、
「ログイン中のユーザー情報(IDや名前など)をどこからでも取得・更新できる」というシナリオで書いてみます。

シングルトン実装コード▼

object UserSessionManager {
    private var userId: String? = null
    private var userName: String? = null

    fun login(id: String, name: String) {
        userId = id
        userName = name
        println("ログイン成功:$userName(ID: $userId)")
    }

    fun logout() {
        println("ログアウトしました:$userName")
        userId = null
        userName = null
    }

    fun isLoggedIn(): Boolean {
        return userId != null
    }

    fun getUserDisplayName(): String {
        return userName ?: "ゲスト"
    }
}

このコードでは、userId や userName といったプロパティを object の中に保持しており、
login()logout() によってその値を更新します。

つまり、一度ログイン処理で userId や userName に値をセットすれば、
どのクラス・どの画面からアクセスしても、常に「最新の状態」のuserId や userNameを取得できるという仕組みです。

これにより、たとえばログイン状態の確認(isLoggedIn())や、表示名の取得(getUserDisplayName())などが、アプリ全体のどこからでも同じように扱えるようになります。

呼び出し側のコード▼

fun main() {
    // ログイン処理
    UserSessionManager.login("user123", "田中 太郎")

    // 別のクラスや場所でも同じインスタンスにアクセス可能
    if (UserSessionManager.isLoggedIn()) {
        println("こんにちは、${UserSessionManager.getUserDisplayName()} さん!")
    }

    // ログアウト処理
    UserSessionManager.logout()
}

companion object(コンパニオンオブジェクト)とは?

companion object は、クラスの内部で定義される「クラスに紐づく“static的な”領域」です。
Javaの static の代わりとして使われ、クラス名から直接呼び出せる関数や定数を定義できます。

class User(val name: String) {
    companion object {
        fun createGuest(): User {
            return User("ゲスト")
        }
    }
}
// クラス名から呼び出し
User.createGuest()

↑Userクラスに関連する便利な処理をまとめたいときは companion object がよい といったイメージです。

object と companion object の違いと使い分け

Kotlinにおける objectcompanion object はどちらも「1つだけのインスタンス(シングルトン)」ですが、目的と使いどころがまったく違います。

objectが「アプリ全体で1つだけ使うための“完全なシングルトン”オブジェクト」であるのに対して
companion objectは「クラスにくっついた“static的な機能”をまとめた領域」となります。

特徴objectcompanion object
どこに書くかクラス外クラス内
何に使うかアプリ全体で使う共有処理やデータクラス専用の便利関数や定数
呼び方object名.メソッド()クラス名.メソッド()
インスタンスが必要か不要(自動で1つだけ作られる)不要(クラスの中に最初からある)

より実践的な例として、以下のように実装してみましょう。▼

シナリオ:ユーザーを作る+お気に入りを管理する

Userクラス:ユーザー1人の情報(名前など)を持つ
object:全ユーザー共通のお気に入り(1つだけ)を管理する
companion object:ユーザーを作るための便利関数(ゲスト/ログインなど)

// お気に入りリスト(全ユーザー共通で1つだけ)
object FavoriteManager {
    private val favoriteItems = mutableListOf<String>()

    // 追加
    fun addFavorite(item: String) {
        favoriteItems.add(item)
    }

    // 情報取得
    fun getFavorites(): List<String> {
        return favoriteItems
    }
}

// ユーザークラス(1人分)
class User(val name: String) {
    // ユーザーのプロフィールを表示
    fun showProfile() {
        println("ユーザー名:$name")
    }

    companion object {
        // ゲストユーザーを作る
        fun createGuest(): User {
            return User("ゲスト")
        }

        // 名前を指定してユーザーを作る
        fun createWithName(name: String): User {
            return User(name)
        }
    }
}

<お気に入りリストへの追加 & 情報取得>
全ユーザー共用 かつ Userがいなくても呼べるのでインスタンス化不要 = object

<ユーザーのプロフィールを表示>
個人の振る舞い かつ Userがいないと呼べないのでインスタンス化必要 = 通常の関数

<ゲストユーザーを作る & 名前を指定してユーザーを作る>
User クラスに関する処理 かつ Userがいなくても呼べるのでインスタンス化不要 = companion object

まとめ

おつかれさまでした。いかがでしたでしょうか!

オブジェクト指向言語を触ったことがある人でも、この辺りの知識を今一度基本から学び直すのも良いですね!

すだ

技術者としてのキャリアパスを次のレベルへと進めたい皆様、
未経験からIT・Webエンジニアを目指すなら【ユニゾンキャリア】
を通じて、
自分の市場価値をさらに向上させてみませんか?

それではまた次回の記事でお会いしましょう!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

弊社テックブログをご愛読いただきありがとうございます。
当テックブログを運用している株式会社メモリアインクは、
【正社員】還元率83%
【フリーランス】マージン一律5万円で案件のご紹介
と、エンジニアの皆様に分かりやすい形で稼げる仕組みを構築し提供させていただいております。

コメント

コメントする

目次