中級: オブジェクト
この章では、オブジェクト宣言を探求することで、クラスの理解を深めます。この知識は、プロジェクト全体の動作を効率的に管理するのに役立ちます。
オブジェクト宣言
Kotlinでは、オブジェクト宣言を使用して、単一のインスタンスを持つクラスを宣言できます。ある意味で、クラスを宣言すると同時に、その単一のインスタンスが作成されます。オブジェクト宣言は、プログラムの単一の参照点として使用するクラスを作成したり、システム全体の動作を調整したりする場合に役立ちます。
TIP
簡単にアクセスできる単一のインスタンスのみを持つクラスは、シングルトンと呼ばれます。
Kotlinのオブジェクトは遅延的であり、アクセスされたときにのみ作成されます。Kotlinはまた、すべてのオブジェクトがスレッドセーフな方法で作成されることを保証するため、手動でこれを確認する必要はありません。
オブジェクト宣言を作成するには、object
キーワードを使用します。
object DoAuth {}
object
の名前の後に、波括弧{}
で定義されたオブジェクト本体内に任意のプロパティまたはメンバ関数を追加します。
NOTE
オブジェクトはコンストラクタを持つことができないため、クラスのようなヘッダを持ちません。
たとえば、認証を担当するDoAuth
というオブジェクトを作成したいとします。
object DoAuth {
fun takeParams(username: String, password: String) {
println("input Auth parameters = $username:$password")
}
}
fun main(){
// takeParams() 関数が呼び出されたときにオブジェクトが作成されます
DoAuth.takeParams("coding_ninja", "N1njaC0ding!")
// input Auth parameters = coding_ninja:N1njaC0ding!
}
このオブジェクトには、username
とpassword
変数をパラメータとして受け取り、コンソールに文字列を返すtakeParams
というメンバ関数があります。DoAuth
オブジェクトは、関数が最初に呼び出されたときにのみ作成されます。
NOTE
オブジェクトはクラスやインターフェースを継承できます。例:
interface 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()
などの追加のメンバ関数が自動的に付属しています。
NOTE
データクラスとは異なり、データオブジェクトは単一のインスタンスしか持たずコピーできないため、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つのクラスにつき1つのコンパニオンオブジェクトしか持てません。コンパニオンオブジェクトは、そのクラスが最初に参照されたときにのみ作成されます。
コンパニオンオブジェクト内で宣言されたプロパティや関数は、すべてのクラスインスタンスで共有されます。
クラス内にコンパニオンオブジェクトを作成するには、オブジェクト宣言と同じ構文を使用しますが、companion
キーワードをプレフィックスとして付けます。
companion object Bonger {}
コンパニオンオブジェクトに名前を付ける必要はありません。名前を定義しない場合、デフォルトはCompanion
になります。
コンパニオンオブジェクトのプロパティや関数にアクセスするには、クラス名を参照します。例:
class BigBen {
companion object Bonger {
fun getBongs(nTimes: Int) {
repeat(nTimes) { print("BONG ") }
}
}
}
fun main() {
// クラスが最初に参照されたときにコンパニオンオブジェクトが作成されます。
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() {
// 各データオブジェクトの名前を出力
println("Order name: $OrderOne")
// Order name: OrderOne
println("Order name: $OrderTwo")
// Order name: OrderTwo
// 注文が同一であるかを確認
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() {
// 各データオブジェクトの名前を出力
println("Order name: $OrderOne")
// Order name: OrderOne
println("Order name: $OrderTwo")
// Order name: OrderTwo
// 注文が同一であるかを確認
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
}