中級:物件
在本章中,您將透過探索物件宣告來擴展對類別的理解。這些知識將幫助您有效地管理專案中的行為。
物件宣告
在 Kotlin 中,您可以使用 物件宣告 (object declarations) 來宣告一個具有單一實例的類別。從某種意義上說,您是同時宣告類別並建立該單一實例。當您想建立一個類別作為程式的單一參考點或協調系統中的行為時,物件宣告非常有用。
TIP
一個只有一個實例且易於存取的類別稱為 單例 (singleton)。
Kotlin 中的物件是惰性 (lazy) 的,這表示它們僅在被存取時才建立。Kotlin 還確保所有物件都以執行緒安全的方式建立,因此您無需手動檢查。
要建立物件宣告,請使用 object
關鍵字:
object DoAuth {}
在您的 object
名稱之後,在大括號 {}
定義的物件主體中新增任何屬性或成員函式。
NOTE
物件不能有建構函式,所以它們不像類別那樣有標頭。
例如,假設您想建立一個名為 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!
}
該物件有一個名為 takeParams
的成員函式,它接受 username
和 password
變數作為參數,並將一個字串輸出到控制台。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 提供了資料物件 (data objects)。與您在初學者指南中學到的資料類別類似,資料物件會自動附帶額外的成員函式: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 中,一個類別可以擁有一個物件:一個伴生物件 (companion object)。每個類別只能有一個伴生物件。伴生物件僅在其類別首次被參考時建立。
伴生物件內部宣告的任何屬性或函式都會在所有類別實例之間共享。
要在類別中建立伴生物件,請使用與物件宣告相同的語法,但前面加上 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
的伴生物件。該伴生物件有一個名為 getBongs()
的成員函式,它接受一個整數,並將 "BONG"
印到控制台與該整數相同次數。
在 main()
函式中,getBongs()
函式透過參考類別名稱被呼叫。伴生物件在此時建立。getBongs()
函式以參數 12
呼叫。
練習
練習 1
您經營一家咖啡店,並有一個追蹤顧客訂單的系統。參考以下程式碼,完成第二個資料物件的宣告,以便 main()
函式中的以下程式碼能成功執行:
|---|---|
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
。在您的物件中實作 name
屬性與 move()
函式,以便 main()
函式中的以下程式碼能成功執行:
|---|---|
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
}