
みなさまこんにちは〜!
メモリアインクのすだです。
今回は、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における object
と companion object
はどちらも「1つだけのインスタンス(シングルトン)」ですが、目的と使いどころがまったく違います。
objectが「アプリ全体で1つだけ使うための“完全なシングルトン”オブジェクト」であるのに対して
companion objectは「クラスにくっついた“static的な機能”をまとめた領域」となります。
特徴 | object | companion 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エンジニアを目指すなら【ユニゾンキャリア】>
自分の市場価値をさらに向上させてみませんか?
それではまた次回の記事でお会いしましょう!
コメント