【Kotlin】data class Data Class vs 一般 Class 自動生成的方法 Data Class: Kotlin 編譯器會自動生成 equals()、 hashCode()、 toString()、 copy() 等方法。 一般 Class: 需要手動實現這些方法,或者使用 IDE 的插件來生成。 不可變性 Data Class: 可以聲明為 val 型,使其成員變量成為只讀。 一般 Class: 需要額外的設計來實現不可變性,如使用 private 成員變量並提供 getter 方法。 Component 函数 Data Class: Kotlin 可以通過 componentN() 函數來分解對象。 一般 Class: 需要手動定義這些分解函數。 自動生成的方法 在這個示例中, Person 是一個 data class,它自動獲得了  equals()、 hashCode()、 toString()、 copy() 等方法,而一般 class,我們需要手動實現方法。  .toString() // Data Class data class Person(val name: String, val age: Int) // 一般 Class class Person2(val name: String, val age: Int) class Person3(val name: String, val age: Int) { override fun toString(): String { return "Person3(name='$name', age=$age)" } } fun main() { val p1 = Person("Mary", 18) val p2 = Person2("John", 24) val p3 = Person3("Sam", 30) println(p1) //Person(name=Mary, age=18) println(p2) //Person2@27bc2616 println(p3) //Person3(name='Sam', age=30) } .equals() data class Person(val name: String){ var age:Int = 0 } data class Person2(val name: String, val age:Int) class Person3(val name: String, val age:Int){ } class Person4(val name: String, val age:Int){ fun equals(p: Person4):Boolean{ return (this.name == p.name) } } fun main() { val jack = Person("Jack") jack.age = 20 println(jack) // Person(name=Jack) println(jack.age) // 20 val sam = Person("Sam") println(sam) // Person(name=sam) sam.age = 21 println(sam.age) // 21 println(sam.equals(jack)) // false val oldSam = Person("Sam") println(oldSam) // Person(name=sam) oldSam.age = 18 println(oldSam.age) // 18 println(oldSam.equals(sam)) // true !!! val mary = Person2("Mary",18) val mary2 = Person2("Mary",18) val mary3 = Person2("Mary",20) println(mary.equals(mary2)) // true println(mary.equals(mary3)) // false val john = Person3("John",35) val john2 = Person3("John",35) println(john.equals(john2)) // false val Jennifer = Person4("Jennifer",12) val Jennifer2 = Person4("Jennifer",12) println(Jennifer.equals(Jennifer2)) // true } .copy() data class Person(val name: String, val age: Int) fun main() { val jack = Person("Jack",4) println(jack) // Person(name=Jack, age=4) val olderJack = jack.copy(age = 9) println(olderJack) // Person(name=Jack, age=9) }   不可變性  可以聲明為 val 型,使其成員變量成為只讀。 data class Person(val name: String, val age: Int) fun main() { val person = Person("Alice", 30) person.name = "John" // 'val' cannot be reassigned. } 一般class 需撰寫 private set class Person { var name: String = "Unknown" private set // 私有的 setter var age: Int = 0 private set // 私有的 setter fun setName(newName: String) { name = newName // 可以在類內部修改 } fun setAge(newAge: Int) { age = newAge // 可以在類內部修改 } } fun main() { val person = Person() person.setName("Mary") person.setAge(18) println(person.name) // Mary println(person.age) // 18 person.name = "Mary" // Cannot access 'name': it is private in '/Person'. } Component 函数 在 Kotlin 中, componentN() 函數是一種特殊的函數,用於解構(分解)對象。解構是一種語法糖,可以讓你將對象的屬性拆分成獨立的變量。這些函數在 data class 中是自動生成的,而在一般的 class 中則需要手動定義。 什麼是解構(Destructuring)? 解構(Destructuring)是一種語法特性,允許將對象的屬性分解成獨立的變量。這在需要從對象中提取多個值時非常有用。 Data Class 的 Component 函數 當你定義一個 data class 時,Kotlin 會自動為每個屬性生成 componentN() 函數。這些函數按照屬性在主構造函數中的聲明順序編號。例如, component1() 對應於第一個屬性, component2() 對應於第二個屬性,以此類推。 data class Person(val name: String, val age: Int) fun main() { val person = Person("Alice", 30) // 使用解構聲明 val (name, age) = person println("Name: $name, Age: $age") // 手動調用 component 函數 val nameComponent = person.component1() val ageComponent = person.component2() println("Name: $nameComponent, Age: $ageComponent") } 在這個示例中, Person 類是一個 data class,所以 Kotlin 會自動生成 component1() 和 component2() 函數。你可以使用解構聲明 val (name, age) = person 來將對象的屬性分解成獨立的變量。 一般 Class 的 Component 函數 對於一般的 class,你需要手動定義 componentN() 函數來實現解構。這意味著你需要自己編寫這些函數,以便在解構時可以使用。 class Car(val brand: String, val year: Int) { operator fun component1() = brand operator fun component2() = year } fun main() { val car = Car("Toyota", 2022) // 使用解構聲明 val (brand, year) = car println("Brand: $brand, Year: $year") // 輸出:Brand: Toyota, Year: 2022 // 手動調用 component 函數 val brandComponent = car.component1() val yearComponent = car.component2() println("Brand: $brandComponent, Year: $yearComponent") // 輸出:Brand: Toyota, Year: 2022 } 賦值解構 val jane = User("Jane", 35) val (name, age) = jane println("$name, $age years of age") // Jane, 35 years of age javascript 也有類似功能 const user = { name: "Jane", age: 35, }; // { name, age } 等同於 { name: name, age: age } const { name, age } = user; console.log(`${name}, ${age} years of age`) // "Jane, 35 years of age" /** operator 除了 componentN 函數外,operator 關鍵字還可以用於重載許多其他運算符 算術運算符 + 對應於 plus - 對應於 minus * 對應於 times / 對應於 div % 對應於 rem 或 mod */ class Show(val liveId: String, val duration: Int, val salesVolume: Int ) { operator fun plus(show: Show): Show { return Show(liveId, duration + show.duration, salesVolume + show.salesVolume) } } fun main() { val show1 = Show("2024071001001001P", 60, 200) val show2 = Show("2024071001001001P", 90, 400) val totalShow = show1 + show2 // 使用重載的加法運算符 println("(${show1.duration}, ${show1.salesVolume})") // (60, 200) println("(${show2.duration}, ${show2.salesVolume})") // (90, 200) println("(${totalShow.duration}, ${totalShow.salesVolume})") // (150, 200) } 建立data class注意事項 為了確保產生的程式碼的一致性和有意義的行為,資料類別必須滿足以下要求: 主構造函數必須至少有一個參數。 所有主要建構函數參數必須標記為 val或 var。 資料類別不能是抽象的(abstract)、開放的(abstract)、密封的(sealed)或內部的(inner)。 不能直接繼承自其他 data class,但可以繼承自普通class或實作介面 此外, data class 的產生遵循以下關於成員繼承的規則: 如果data class 本身有 .equals()、 .hashCode()、 .toString() 明確實作或父類別有的實作,則不會產生這些函數,而是使用現有的實作。 不允許為 .componentN()和 .copy() 函數提供實作。 data class toString 改用 json 輸出 導入 Gson 庫:首先需要將 Gson 添加到你的專案依賴中。 dependencies { implementation 'com.google.code.gson:gson:2.8.8' } 覆寫toString() import com.google.gson.Gson data class Person(val name: String, val age:Int){ override fun toString():String{ val gson = Gson() return gson.toJson(this) } } fun main(args: Array) { val mary = Person("Mary",18) println(mary) } // {"name":"Mary","age":18}