中級: オブジェクト
この章では、オブジェクト宣言について掘り下げてクラスの理解を深めます。この知識は、プロジェクト全体で動作を効率的に管理するのに役立ちます。
オブジェクト宣言
Kotlinでは、オブジェクト宣言を使用して単一のインスタンスを持つクラスを宣言できます。ある意味、クラスを宣言すると同時に単一のインスタンスを作成します。オブジェクト宣言は、プログラムの単一の参照ポイントとして使用するクラスを作成したり、システム全体で動作を連携させたりする場合に便利です。
容易にアクセス可能な単一のインスタンスのみを持つクラスは、シングルトンと呼ばれます。
Kotlinのオブジェクトは遅延初期化されます。つまり、アクセスされたときにのみ作成されます。また、Kotlinはすべてのオブジェクトがスレッドセーフな方法で作成されることを保証するため、手動でこれを確認する必要はありません。
オブジェクト宣言を作成するには、object
キーワードを使用します。
object DoAuth {}
object
名の後に、波括弧 {}
で定義されたオブジェクト本体内にプロパティやメンバー関数を追加します。
オブジェクトはコンストラクタを持つことができないため、クラスのようにヘッダーを持ちません。
例えば、認証を担当するDoAuth
というオブジェクトを作成したいとします。
object DoAuth {
fun takeParams(username: String, password: String) {
println("input Auth parameters = $username:$password")
}
}
fun main(){
// The object is created when the takeParams() function is called
DoAuth.takeParams("coding_ninja", "N1njaC0ding!")
// input Auth parameters = coding_ninja:N1njaC0ding!
}
このオブジェクトには、username
とpassword
変数をパラメータとして受け取り、コンソールに文字列を出力するtakeParams
というメンバー関数があります。DoAuth
オブジェクトは、この関数が初めて呼び出されたときにのみ作成されます。
オブジェクトはクラスやインターフェースを継承できます。例:
kotlininterface Auth { fun takeParams(username: String, password: String) } object DoAuth : Auth { override fun takeParams(username: String, password: String) { println("input Auth parameters = $username:$password") } }
データオブジェクト
オブジェクト宣言の内容をより簡単に表示できるように、Kotlinにはデータオブジェクトがあります。入門ツアーで学習したデータクラスと同様に、データオブジェクトにはtoString()
とequals()
という追加のメンバー関数が自動的に付属します。
データクラスとは異なり、データオブジェクトにはコピーできない単一のインスタンスしかないため、
copy()
メンバー関数が自動的に付属することはありません。
データオブジェクトを作成するには、オブジェクト宣言と同じ構文を使用しますが、data
キーワードを前に付けます。
data object AppConfig {}
例:
data object AppConfig {
var appName: String = "My Application"
var version: String = "1.0.0"
}
fun main() {
println(AppConfig)
// AppConfig
println(AppConfig.appName)
// My Application
}
コンパニオンオブジェクト
Kotlinでは、クラスはオブジェクト、すなわちコンパニオンオブジェクトを持つことができます。クラスごとに1つだけコンパニオンオブジェクトを持つことができます。コンパニオンオブジェクトは、そのクラスが初めて参照されたときにのみ作成されます。
コンパニオンオブジェクト内で宣言されたプロパティや関数は、すべてのクラスインスタンス間で共有されます。
クラス内でコンパニオンオブジェクトを作成するには、オブジェクト宣言と同じ構文を使用しますが、companion
キーワードを前に付けます。
companion object Bonger {}
コンパニオンオブジェクトは名前を持つ必要はありません。定義しない場合、デフォルトは
Companion
です。
コンパニオンオブジェクトのプロパティや関数にアクセスするには、クラス名を参照します。例:
class BigBen {
companion object Bonger {
fun getBongs(nTimes: Int) {
repeat(nTimes) { print("BONG ") }
}
}
}
fun main() {
// Companion object is created when the class is referenced for the
// first time.
BigBen.getBongs(12)
// BONG BONG BONG BONG BONG BONG BONG BONG BONG BONG BONG BONG
}
この例では、BigBen
というクラスを作成し、Bonger
というコンパニオンオブジェクトを含んでいます。このコンパニオンオブジェクトには、整数を受け取り、その整数と同じ回数だけコンソールに"BONG"
と出力するgetBongs()
というメンバー関数があります。
main()
関数では、クラス名を参照してgetBongs()
関数が呼び出されます。この時点でコンパニオンオブジェクトが作成されます。getBongs()
関数はパラメータ12
で呼び出されます。
演習
演習1
あなたはコーヒーショップを経営しており、顧客の注文を追跡するシステムを持っています。以下のコードを考慮し、main()
関数内の以下のコードが正常に実行されるように、2番目のデータオブジェクトの宣言を完成させてください。
interface Order {
val orderId: String
val customerName: String
val orderTotal: Double
}
data object OrderOne: Order {
override val orderId = "001"
override val customerName = "Alice"
override val orderTotal = 15.50
}
data object // Write your code here
fun main() {
// Print the name of each data object
println("Order name: $OrderOne")
// Order name: OrderOne
println("Order name: $OrderTwo")
// Order name: OrderTwo
// Check if the orders are identical
println("Are the two orders identical? ${OrderOne == OrderTwo}")
// Are the two orders identical? false
if (OrderOne == OrderTwo) {
println("The orders are identical.")
} else {
println("The orders are unique.")
// The orders are unique.
}
println("Do the orders have the same customer name? ${OrderOne.customerName == OrderTwo.customerName}")
// Do the orders have the same customer name? false
}
解答例
interface Order {
val orderId: String
val customerName: String
val orderTotal: Double
}
data object OrderOne: Order {
override val orderId = "001"
override val customerName = "Alice"
override val orderTotal = 15.50
}
data object OrderTwo: Order {
override val orderId = "002"
override val customerName = "Bob"
override val orderTotal = 12.75
}
fun main() {
// Print the name of each data object
println("Order name: $OrderOne")
// Order name: OrderOne
println("Order name: $OrderTwo")
// Order name: OrderTwo
// Check if the orders are identical
println("Are the two orders identical? ${OrderOne == OrderTwo}")
// Are the two orders identical? false
if (OrderOne == OrderTwo) {
println("The orders are identical.")
} else {
println("The orders are unique.")
// The orders are unique.
}
println("Do the orders have the same customer name? ${OrderOne.customerName == OrderTwo.customerName}")
// Do the orders have the same customer name? false
}
演習2
Vehicle
インターフェースを継承するオブジェクト宣言を作成し、FlyingSkateboard
というユニークな乗り物の種類を作成してください。main()
関数内の以下のコードが正常に実行されるように、オブジェクト内でname
プロパティとmove()
関数を実装してください。
interface Vehicle {
val name: String
fun move(): String
}
object // Write your code here
fun main() {
println("${FlyingSkateboard.name}: ${FlyingSkateboard.move()}")
// Flying Skateboard: Glides through the air with a hover engine
println("${FlyingSkateboard.name}: ${FlyingSkateboard.fly()}")
// Flying Skateboard: Woooooooo
}
解答例
interface Vehicle {
val name: String
fun move(): String
}
object FlyingSkateboard : Vehicle {
override val name = "Flying Skateboard"
override fun move() = "Glides through the air with a hover engine"
fun fly(): String = "Woooooooo"
}
fun main() {
println("${FlyingSkateboard.name}: ${FlyingSkateboard.move()}")
// Flying Skateboard: Glides through the air with a hover engine
println("${FlyingSkateboard.name}: ${FlyingSkateboard.fly()}")
// Flying Skateboard: Woooooooo
}
演習3
温度を記録するアプリがあるとします。クラス自体は情報を摂氏で保存しますが、華氏でもインスタンスを簡単に作成できる方法を提供したいと考えています。main()
関数内の以下のコードが正常に実行されるように、データクラスを完成させてください。
ヒント
data class Temperature(val celsius: Double) {
val fahrenheit: Double = celsius * 9 / 5 + 32
// Write your code here
}
fun main() {
val fahrenheit = 90.0
val temp = Temperature.fromFahrenheit(fahrenheit)
println("${temp.celsius}°C is $fahrenheit °F")
// 32.22222222222222°C is 90.0 °F
}
解答例
data class Temperature(val celsius: Double) {
val fahrenheit: Double = celsius * 9 / 5 + 32
companion object {
fun fromFahrenheit(fahrenheit: Double): Temperature = Temperature((fahrenheit - 32) * 5 / 9)
}
}
fun main() {
val fahrenheit = 90.0
val temp = Temperature.fromFahrenheit(fahrenheit)
println("${temp.celsius}°C is $fahrenheit °F")
// 32.22222222222222°C is 90.0 °F
}