【Kotlin】第12課-資料類別(Data Class)

我們經常宣告類別來裝載物件的資料,例如前面課程範例中的產品、課程、學生。若在宣告類別時加上「data」關鍵字,將會有特別的效果,例如隱含地覆寫或產生一些方法。本文會介紹由資料類別自行產生與覆寫的方法,另外也會提到物件的複製與解構。

一、toString 方法

假設我們有下列兩個看似一樣的產品類別。雖然屬性相同,但第二個被加上了 data 關鍵字,成為資料類別。

class Product(val id: String,
var name: String,
var price: Int) {
}

data class DataProduct(val id: String,
var name: String,
var price: Int) {
}

接下來建立屬性值完全相同的兩個物件,並觀察 toString 方法的回傳值。這時會發現資料類別的物件是以屬性與值的格式呈現,容易閱讀。至於一般類別的物件,回傳值則是類別名稱與雜湊值(hashCode)的十六進位。

fun main(args: Array<String>) {
val product1 = Product("BK001", "數學複習講義", 320)
val product2 = DataProduct("BK001", "數學複習講義", 320)
println(product1.toString())
println(product2.toString())
}

資料類別的主要建構式,規定參數必須都用 var 與 val 來宣告。意即參數都會成為類別的屬性,但次要建構式則沒有這項限制。資料類別的 toString 方法輸出時,只會讀取主要建構式的參數。下面介紹的其他方法也是如此,沒有宣告在主要建構式的屬性並不會參與程式中的運算。


二、複製物件

資料類別的物件可以進行複製,藉此產生另一個屬性值相同的物件。Kotlin 的複製類似 Java 的 clone,因此兩者的記憶體位址不同。想要複製一個資料類別的物件,只要呼叫 copy 方法即可。如果有需要,也能傳入主要建構式的參數,調整部份的屬性值。

val product1 = DataProduct("BK001", "數學複習講義", 320)
val product2 = product1.copy()
val product3 = product1.copy(id = "FN001", name = "中等會計學")

三、解構

資料類別還具有「component」方法,例如 component1component2 等,與主要建構式的屬性數量相同。它們的回傳值正好就是類別的屬性值,依照屬性的宣告順序一一對應。

val product = DataProduct("BK001", "數學複習講義", 320)

// 印出:BK001
println(product.component1())

// 印出:數學複習講義
println(product.component2())

// 印出:320
println(product.component3())

這個「component」方法會在「解構」(destructuring)時被呼叫。解構可以聯想成建構式的反運算,意思是將物件還原成它目前的屬性值。

val product = DataProduct("BK001", "數學複習講義", 320)
val (productId, productName, productPrice) = product

// 印出:BK001
println(productId)

// 印出:數學複習講義
println(productName)

// 印出:320
println(productPrice)

在宣告變數時用括號將變數包起來,再將資料類別的物件直接賦予給它們,這樣就能進行解構。這些變數會依照主要建構式的屬性宣告順序被賦值。

四、equals 與 hashCode 方法

資料類別會覆寫 equalshashCode 方法,使得判斷與計算的依據轉而採取屬性值。因此屬性值相同的物件,equals 方法的結果為 true,而 hashCode 方法的結果也一致。

val product1 = DataProduct("BK001", "數學複習講義", 320)
val product2 = DataProduct("BK001", "數學複習講義", 320)

// 印出:true
println(product1.equals(product2))
println(product2.equals(product1))

// 印出:-1538320945
println(product1.hashCode())
println(product2.hashCode())

至於一般類別的物件,即便屬性值相同,equals 方法仍回傳 false,代表兩者為不同的物件。而它們也具有不同的 hash code

val product3 = Product("BK001", "數學複習講義", 320)
val product4 = Product("BK001", "數學複習講義", 320)

// 印出:false
println(product3.equals(product4))
println(product4.equals(product3))

// 兩者 hash code 不同
println(product3.hashCode())
println(product4.hashCode())

另外,在物件的比較方面,equals 方法可替換成「==」語法。而 Kotlin 提供的「===」語法是用來比較兩個物件的記憶體位址。

若想讓一般類別也能如此判斷物件是否相等,讀者可以在類別按下滑鼠右鍵,選取「Generate  / equals() and hashCode()」。接著選取類別屬性後,IntelliJ 就會幫我們覆寫這兩個方法。其產生的程式碼便是按照屬性來做判斷與計算,與資料類別的理念相同。

class Product(val id: String,
var name: String,
var price: Int) {

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false

other as Product

if (id != other.id) return false
if (name != other.name) return false
if (price != other.price) return false

return true
}

override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + name.hashCode()
result = 31 * result + price
return result
}
}

上一篇:【Kotlin】第11課-單例與伴生物件

留言