コンパニオンオブジェクト
CoddyのKotlinジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 13/57。
一部のメンバーは、1つのインスタンスではなく、クラス全体に属します。Kotlinでは、クラス内のcompanion objectにそれらを配置し、クラス名を通して呼び出します。
class Temperature(val celsius: Double) {
companion object {
fun fromFahrenheit(f: Double) = Temperature((f - 32) * 5 / 9)
}
}mainの中では:
val boiling = Temperature.fromFahrenheit(212.0)
println(boiling.celsius)出力:
100.0fromFahrenheit はファクトリ関数です。つまり、インスタンスを作成する関数です。コンストラクターとは異なり、説明的な名前を持ち、オブジェクトの代わりに null を返すこともできます。private constructor を使うと、ファクトリだけが生成への入口となるため、最初に入力を確認して整理できます。
class Username private constructor(val text: String) {
companion object {
fun of(raw: String): Username? {
val clean = raw.trim().lowercase()
return if (clean.length in 3..12) Username(clean) else null
}
}
}main の中では次のようにします。
println(Username.of(" Ada_L ")?.text)
println(Username.of("x")?.text)
// Username("x") // エラー: コンストラクタは private です出力:
ada_l
nullコンパニオンオブジェクトはクラスに属しているため、privateコンストラクターを呼び出すことができ、クラス自身のコードでは接頭辞なしでコンパニオンのメンバーを使用できます。ここでは、すべてのチケットが、全チケットで共有される1つのカウンターから次の番号を取得します。
class Ticket(val holder: String) {
val number = nextNumber++
companion object {
private var nextNumber = 1
val issued: Int
get() = nextNumber - 1
}
}mainの中では:
val a = Ticket("Ada")
val b = Ticket("Bo")
println("${a.holder} #${a.number}, ${b.holder} #${b.number}")
println("issued: ${Ticket.issued}")出力:
Ada #1, Bo #2
issued: 2Kotlinにはstaticキーワードがありません。コンパニオンのメンバーがその役割を果たします。クラス全体で1つのコピーがあり、ClassName.memberとしてアクセスします。通常のオブジェクトと同様に、コンパニオンはクラスが初めて使用されたときに1度だけ作成されます。ファクトリや共有カウンターなど、1つのインスタンスに依存しないものをここに置きます。インスタンスのデータを読み取るものはすべて、通常のメンバーのままにします。
class User private constructor(val name: String) {
fun greet() = "hi $name" // インスタンスが必要: user.greet()
companion object {
fun guest() = User("guest") // クラスに属する: User.guest()
}
}チャレンジ
簡単Coupon クラスを完成させてください。コンストラクターは private なので、クーポンはコンパニオンオブジェクト内のファクトリー Coupon.create(code, percent) で作成します。コードが大文字の英字と数字で構成された 4~10 文字で、percent が 1~90 の範囲内でない限り、null を返します。コンパニオンオブジェクトは、作成したクーポンの数も created に記録します。この値は外部のコードから読み取れますが、変更することはできません。apply(price) は割引分を差し引いた価格を切り捨てて返します(price * (100 - percent) / 100)。
用意されているコードは、行 SAVE10 10 200(コード、percent、価格)を読み取ります。SAVE10: 180 または invalid coupon SAVE10 を出力し、最後に件数を付けて created: を出力します。
コードは Coupon.kt に記述してください。Main.kt には用意された入出力コードが含まれており、編集できません。
自分で試してみよう
fun main() {
// 提供された入出力コード: そのままにしておく
val input = generateSequence(::readLine).toList()
for (line in input) {
val (code, percent, price) = line.split(" ")
val coupon = Coupon.create(code, percent.toInt())
if (coupon == null) println("invalid coupon $code") else println("${coupon.code}: ${coupon.apply(price.toInt())}")
}
println("created: ${Coupon.created}")
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
自分で練習してみよう: Kotlinプレイグラウンド