[Day 07] 遠征 Kotlin × 類別與物件
類別定義
Kotlin 在物件導向這塊與其他程式語言類似,類別上也包含建構式、函式、屬性、物件宣告等,而所謂類別就像一張藍圖,以蓋房子為例,它只是給予我們如何蓋出房子的細節,並非是一棟蓋好的房子。
在 Kotlin 使用關鍵字 class
宣告類別,主要包含兩類內容:行為
和 資料
,以 類別函數
定義類別的行為
,以 類別屬性
增加類別的 資料
,操作如下範例所示:
我們可以透過下面範例觀察出三件事
- 在定義類別時,我們會使用到
class
關鍵字進行定義
- 在呼叫類別時,不會像 Java 需要使用
new
關鍵字進行實現(Instance),而是直接使用類別名稱再加上括號 ( ) 即可
- 在建立屬性時,若我們有
取值 get
或 賦值 set
需求時,不需要像 Java 必須在類別裡面建立 getter
與 setter
,這些屬性的 getter
與 setter
是 Kotlin 編譯器為我們自動產生的,讓我們在程式碼中保持
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| fun main() { Person().printName() }
class Person {
var userName: String = "user"
fun printName() { println("Name is $userName") } }
|
在 Kotlin 中,如果一個類別是空的,沒有內容,括號是可以直接省略的
建構函數 Constructor
在 Java 的建構函數(Constructor)可以讓我們建構出多個不同參數的建構函數,但是 Java 每個建構函數都是同級別的,而在 Kotlin 中卻是分成兩級運算子(主建構函數與次建構函數),主建構函數是直接包含在類別名稱之後,次建構函數則是在類別裡面進行實現,我們利用下面範例進行觀察:
下面範例是當我們加入主建構函數時的程式操作狀況
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| fun main() { val person = Person("devin", "abc@gmail.com") print(person.name) }
class Person(_name: String, _email: String) { var name = _name get() = field.capitalize() set(value) { field = value.trim() } var email = _email }
|
下面範例是加入次建構函數時,增加初始值設定與初始邏輯判斷
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| fun main() { val person = Person() print(person.name)
}
class Person(_name: String, _email: String) { var name = _name get() = field.capitalize() set(value) { field = value.trim() } var email = _email
constructor() : this(_name = "路人甲", _email = "") { if (name == "路人甲") { println("使用者未輸入參數") } } }
|
在上面範例中,我們在次建構函數使用了預設參數方法,但實際上此方法在主建構函數與次建構函數都可以使用,我們也可以將上面範例的預設值改為在主建構函數使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| fun main() { val person = Person() print(person.name)
}
class Person(_name: String = "路人甲", _email: String) { var name = _name get() = field.capitalize() set(value) { field = value.trim() } var email = _email
constructor() : this(_email = "") { if (name == "路人甲") { println("使用者未輸入參數") } } }
|
除了主次建構函數可設置初始值以外,我們也可以另外為函數定義一個初始化區塊 init,此區塊除了設定初始值以外,也可以進行數值的有效性檢查,可觀察以下範例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| fun main() { val person = Person("","") print(person.name)
}
class Person(_name: String, _email: String) { var name = _name get() = field.capitalize() set(value) { field = value.trim() } var email = _email
init { name = "路人甲" require(name.isNotBlank()) { "使用者未輸入姓名參數" } } }
|
當我們在呼叫類別時,可加入有效值判斷,當檢查不通過時,會如下面圖片呈現出 IllegalArgumentException 的錯誤訊息

物件(Object)介紹
前面我們介紹類別(Class)的介紹,我們會發現,假設我們有很多個類別需求,只需多次呼叫類別即可,但產生多個類別的時候,我們可能會遇到一個問題-如何進行類別之間的資料溝通,此時我們就必須要為這樣的需求進行處理。
而假設我們需求只想要使用一個實例(Instance)來管理整個程式的狀態,我們就可以定義一個單例(Singleton)即可,而在 Kotlin 程式語言,根據上述需求,我們可以使用 object 關鍵字進行定義出一個在應用程式中只有它存在的實例
故我們可以歸納出物件(Object)有幾個特性:
- 整個應用程式中只會存在一個實例(Instance)
- 相當於 Singleton 設計模式
物件宣告
物件宣告主要會利用 object
關鍵字進行定義物件(Object),在定義上類似於類別,也有初始區塊、屬性資料等操作方法,相對於類別,物件可自動實例化(Instance),但在 object
無法使用建構函數 Constructor,即無法在初始化時從外部傳遞參數進行實現,但我們利用一個範例進行觀察:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| fun main() { Family }
object Family { private val person = Person(_name = "devin")
init { println("歡迎來到 Family 家族") printFamilyMember() }
private fun printFamilyMember() { print("目前家族成員有:") println(person.name) } }
|
物件運算
原本使用類別進行處理很重要,能夠幫助我們減少重複邏輯一再出現,而將邏輯抽象為一個新事物概念,但往往有時候需求上不見得都會有重複使用的狀況發生,有時候只會有一次性使用,這時候object 就可以幫助我們進行處理這樣的情境,將 object 作為匿名類別
來使用,可參考下面範例:
1 2 3 4 5 6 7 8
| fun main() { val person = object { var userName: String = "Devin" var email: String = "abc@gmail.com" } println(person.userName) }
|
伴生物件
如果我們在開發上想要把類別實現
與物件初始化
綁在一起,此時就可以考慮使用伴生物件,使用 companion
關鍵字,我們直接利用下面範例進行觀察:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| fun main() { Person.data.printUserName() }
class Person(_name: String = "", _email: String = "") { val name: String = _name val email: String = _email
companion object { val data = Person("Devin", "abc@gmail.com") }
fun printUserName(){ println(name) } }
|
資料類別
在物件導向程式設計中,我們經常會建立專門儲存資料的類別,再將此類別進行實例化物件進行資料溝通,此物件我們會稱為資料傳輸物件
(Data Transfer Object, DTO),在 Kotlin 中特別針對此物件設計一個「資料類別(Data Class)」,我們直接用一個範例來示範:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| fun main() { val person1 = Person("devin", "abc@gmail.com") println("person1 姓名:${person1.name}")
val person2 = person1.copy() println("person2 姓名:${person2.name}")
val (name, email) = person2 println("解構輸出姓名:${name}")
}
data class Person( val name: String, val email: String )
|