iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 7
0

類別定義

Kotlin 在物件導向這塊與其他程式語言類似,類別上也包含建構式、函式、屬性、物件宣告等,而所謂類別就像一張藍圖,以蓋房子為例,它只是給予我們如何蓋出房子的細節,並非是一棟蓋好的房子。

在 Kotlin 使用關鍵字 class 宣告類別,主要包含兩類內容:行為資料,以 類別函數 定義類別的行為,以 類別屬性 增加類別的 資料,操作如下範例所示:

我們可以透過下面範例觀察出三件事

  1. 在定義類別時,我們會使用到 class 關鍵字進行定義
  2. 在呼叫類別時,不會像 Java 需要使用 new 關鍵字進行實現(Instance),而是直接使用類別名稱再加上括號 ( ) 即可
  3. 在建立屬性時,若我們有 取值 get賦值 set 需求時,不需要像 Java 必須在類別裡面建立 gettersetter,這些屬性的 gettersetter 是 Kotlin 編譯器為我們自動產生的,讓我們在程式碼中保持
fun main() {
    // 呼叫類別與呼叫 printName 函數
    Person().printName()
}

// 建立 Person 類別(Class)
class Person {

    // 建立 userName 屬性(Property)
    var userName: String = "user"

    // 建立 printName 函數(Function)
    fun printName() {
        println("Name is $userName")
    }
}

在 Kotlin 中,如果一個類別是空的,沒有內容,括號是可以直接省略的

class Person

建構函數 Constructor

在 Java 的建構函數(Constructor)可以讓我們建構出多個不同參數的建構函數,但是 Java 每個建構函數都是同級別的,而在 Kotlin 中卻是分成兩級運算子(主建構函數與次建構函數),主建構函數是直接包含在類別名稱之後,次建構函數則是在類別裡面進行實現,我們利用下面範例進行觀察:

下面範例是當我們加入主建構函數時的程式操作狀況

fun main() {
	// 在呼叫類別時,傳進去的參數是由主建構函式進行定義
    val person = Person("devin", "abc@gmail.com")
    print(person.name) // 印出 Devin
}

// 主建構函式定義臨時變數 _name 與 _email
class Person(_name: String, _email: String) {
    var name = _name
        // 呼叫 capitalize 方法會為數值設定為首字大寫
        get() = field.capitalize() 
        set(value) {
            // 呼叫 trim 方法會為傳進來的數值去除空白再儲存
            field = value.trim() 
        }
    var email = _email
}

下面範例是加入次建構函數時,增加初始值設定與初始邏輯判斷

fun main() {
    val person = Person()
    print(person.name) 
    
    // 印出下列結果
    // 使用者未輸入參數
    // 路人甲
}

class Person(_name: String, _email: String) {
    var name = _name
        // 呼叫 capitalize 方法會為數值設定為首字大寫
        get() = field.capitalize() 
        set(value) {
            // 呼叫 trim 方法會為傳進來的數值去除空白再儲存
            field = value.trim() 
        }
    var email = _email

    // 若主建構函數沒有帶入參數,則自動帶入預設值
    constructor() : this(_name = "路人甲", _email = "") {
        // 帶入初始邏輯條件
        if (name == "路人甲") {
            println("使用者未輸入參數")
        }
    }
}

在上面範例中,我們在次建構函數使用了預設參數方法,但實際上此方法在主建構函數與次建構函數都可以使用,我們也可以將上面範例的預設值改為在主建構函數使用:

fun main() {
    val person = Person()
    print(person.name) 
    
    // 印出下列結果
    // 使用者未輸入參數
    // 路人甲
}

// 修改主建構函數預設值
class Person(_name: String = "路人甲", _email: String) {
    var name = _name
        // 呼叫 capitalize 方法會為數值設定為首字大寫
        get() = field.capitalize() 
        set(value) {
            // 呼叫 trim 方法會為傳進來的數值去除空白再儲存
            field = value.trim() 
        }
    var email = _email

    constructor() : this(_email = "") {
        // 帶入初始邏輯條件
        if (name == "路人甲") {
            println("使用者未輸入參數")
        }
    }
}

除了主次建構函數可設置初始值以外,我們也可以另外為函數定義一個初始化區塊 init,此區塊除了設定初始值以外,也可以進行數值的有效性檢查,可觀察以下範例:

fun main() {
    val person = Person("","")
    print(person.name)

    // 印出下列結果
    // 使用者未輸入參數
    // 路人甲
}

class Person(_name: String, _email: String) {
    var name = _name
        // 呼叫 capitalize 方法會為數值設定為首字大寫
        get() = field.capitalize() 
        set(value) {
            // 呼叫 trim 方法會為傳進來的數值去除空白再儲存
            field = value.trim() 
        }
    var email = _email

	// 利用初始化區塊(init)進行初始值設定與有效值檢查
    init {
        name = "路人甲"
        // 帶入參數檢查判斷,若檢查不通過,則拋出 IllegalArgumentException 異常
		// 異常結果可看下面圖片顯示
		require(name.isNotBlank()) { "使用者未輸入姓名參數" }
    }
}

當我們在呼叫類別時,可加入有效值判斷,當檢查不通過時,會如下面圖片呈現出 IllegalArgumentException 的錯誤訊息

https://ithelp.ithome.com.tw/upload/images/20200916/201211799cesvTZGJy.png

物件(Object)介紹

前面我們介紹類別(Class)的介紹,我們會發現,假設我們有很多個類別需求,只需多次呼叫類別即可,但產生多個類別的時候,我們可能會遇到一個問題-如何進行類別之間的資料溝通,此時我們就必須要為這樣的需求進行處理。

而假設我們需求只想要使用一個實例(Instance)來管理整個程式的狀態,我們就可以定義一個單例(Singleton)即可,而在 Kotlin 程式語言,根據上述需求,我們可以使用 object 關鍵字進行定義出一個在應用程式中只有它存在的實例

故我們可以歸納出物件(Object)有幾個特性:

  • 整個應用程式中只會存在一個實例(Instance)
  • 相當於 Singleton 設計模式

物件宣告

物件宣告主要會利用 object 關鍵字進行定義物件(Object),在定義上類似於類別,也有初始區塊、屬性資料等操作方法,相對於類別,物件可自動實例化(Instance),但在 object 無法使用建構函數 Constructor,即無法在初始化時從外部傳遞參數進行實現,但我們利用一個範例進行觀察:

fun main() {
    // 呼叫Family object
    Family
}

// 建立物件
object Family {
    // 建立 object 資料
    private val person = Person(_name = "devin")

    // 初始區塊
    init {
        println("歡迎來到 Family 家族")
        printFamilyMember()
    }

    // 建立 object function
    private fun printFamilyMember() {
        print("目前家族成員有:")
        println(person.name)
    }
}

物件運算

原本使用類別進行處理很重要,能夠幫助我們減少重複邏輯一再出現,而將邏輯抽象為一個新事物概念,但往往有時候需求上不見得都會有重複使用的狀況發生,有時候只會有一次性使用,這時候object 就可以幫助我們進行處理這樣的情境,將 object 作為匿名類別來使用,可參考下面範例:

fun main() {
    // 建立 object 匿名類別
    val person = object {
        var userName: String = "Devin"
        var email: String = "abc@gmail.com"
    }
    println(person.userName) // 輸出「Devin」
}

伴生物件

如果我們在開發上想要把類別實現物件初始化綁在一起,此時就可以考慮使用伴生物件,使用 companion 關鍵字,我們直接利用下面範例進行觀察:

fun main() {
	// 呼叫 Person 類別的伴生物件,再呼叫類別方法
    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)
    }
}

// 範例輸出結果為 Devin 

資料類別

在物件導向程式設計中,我們經常會建立專門儲存資料的類別,再將此類別進行實例化物件進行資料溝通,此物件我們會稱為資料傳輸物件(Data Transfer Object, DTO),在 Kotlin 中特別針對此物件設計一個「資料類別(Data Class)」,我們直接用一個範例來示範:

fun main() {
	// 實例化 person 物件
    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}")

    // 最後輸出結果
    // person1 姓名:devin
    // person2 姓名:devin
    // 解構輸出姓名:devin
}

// 建立 Person 資料類別
data class Person(
    val name: String,
    val email: String
)

上一篇
[Day 06] 遠征 Kotlin × Collections 介紹
下一篇
[Day 08] 遠征 Kotlin × 類別繼承、介面、抽象
系列文
30天從零撰寫 Kotlin 語言並應用於 Spring Boot 開發30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言