はじめに
androidアプリを自作する中でKotlinについて学んだことを備忘として記事にします。
本記事ではCompanion objects
の特徴について記載します。
Object について
Companion objects
を説明する前に、まずはObject
の概念について説明します。
KotlinではObject
を使うことでクラスを定義し、さらにそのインスタンスを1ステップで作成できます。
これは、再利用可能なシングルトンインスタンスや1回限りのオブジェクトが必要な場合に便利です。
特徴
- クラス定義からインスタンス定義までを1ステップで作成
- 生成されるインスタンスはシングルトンインスタンスとなる
// データプロバイダーを管理するためのシングルトンオブジェクトを宣言
object DataProviderManager {
// プライベートな変数
private val providers = mutableListOf<String>()
// プロバイダーを登録
fun registerDataProvider(provider: String) {
providers.add(provider)
}
// 登録されたプロバイダーを取得
val allDataProviders: Collection<String>
get() = providers
}
// 使用例
fun main() {
DataProviderManager.registerDataProvider("provider")
print(DataProviderManager.allDataProviders)
}
// 実行結果
// [provider]
クラス内のObjectについて
クラス内でもObject
が定義できます。
Object
の内部変数はクラス内でも使用できます。
ただし、クラスのメンバー変数はObject
内では使用できません。
(Object
はクラスインスタンス生成前に利用できますが、クラスのメンバー変数は生成後にしか利用できないためです)
class Food {
fun cooking() {
// Object内の変数はクラス内でアクセスできる
println("cooking: ${BeefObject.GRAM}")
}
// シングルトンインスタンスとして生成される
object BeefObject {
const val GRAM = 200L
private const val BEEF = "beef"
fun getBeef(): String {
return BEEF
}
}
}
// 使用例
fun main() {
val food = Food()
food.cooking()
println(Food.BeefObject.GRAM) // インスタンス生成済みのため、
println(Food.BeefObject.getBeef()) // クラスをnewすることなく利用できる
}
// 実行結果
// cooking: 200
// 200
// beef
Companion objectsについて
クラス内で定義するObject
と基本的には同じですが、Companion objects
は1クラス1つだけしか定義できません。
用途として、KotlinにはJavaのstaticの概念が存在しないため、その代替としてCompanion objects
という同伴オブジェクトを使用して静的メンバーを定義することができます。
特徴
- クラス内のObject定義と機能的には変わらない
- 1クラスに1つの
Companion objects
しか定義できない制約がある - Javaのstatic変数のような静的な定数などを定義するのに利用できる
class Food {
fun cooking() {
// Object内の変数に直接アクセスできる
println("cooking: $GRAM")
}
// シングルトンインスタンスとして生成される
companion object {
const val GRAM = 300L
private const val PORK = "pork"
fun getPork(): String {
return PORK
}
}
}
// 使用例
fun main() {
val food = Food()
food.cooking()
println("With companion : ${Food.Companion.GRAM}")
println("With companion : ${Food.Companion.getPork()}")
println("Without companion : ${Food.GRAM}") // Companion無しでも呼べる
println("Without companion : ${Food.getPork()}") // Companion無しでも呼べる
}
// 実行結果
// cooking: 300
// With companion : 300
// With companion : pork
// Without companion : 300
// Without companion : pork
Object と Companion objects の違い
機能的には違いはなさそうですが、シングルトンオブジェクトが生成されるタイミングが異なります。
宣言 | オブジェクト生成タイミング |
---|---|
Companion objects | クラスロードされたときに生成される |
クラス内Object宣言 | 自分自身が参照されたときに生成される |
class Food {
companion object Beef {
init { println("初期化 = Beef") }
fun hello() = println("Beef!!")
}
object Pork {
init { println("初期化 = Pork") }
fun hello() = println("Pork!!")
}
}
fun main() {
println("------------- Food クラスのインスタンス化前")
val obj = Food()
println("------------- Food クラスのインスタンス化後")
println("------------- Beef Companion objects の hello() 呼び出し")
Food.Beef.hello()
println("------------- Pork Object の hello() 呼び出し")
Food.Pork.hello()
}
// 実行結果
// ------------- Food クラスのインスタンス化前
// 初期化 = Beef
// ------------- Food クラスのインスタンス化後
// ------------- Beef Companion objects の hello() 呼び出し
// Beef!!
// ------------- Pork Object の hello() 呼び出し
// 初期化 = Pork
// Pork!!
Companion objects
のBeef
インスタンスは、外側のクラスのFood
がインスタンス化されるときに生成されています。
一方で、オブジェクト宣言されたPork
オブジェクトはFood.Pork.hello()
関数が呼び出された時点で初めて生成されます。
まとめ
単純にシングルトンインスタンスを生成したい場合はクラス内ではなく外側にObject
で定義しても良いかと思います。
Companion objects
はJavaのstaticの代替として利用する利用法がよさそうです。