Kotlin Tutorial(11)類別與物件 by Michael | CodeData
top

Kotlin Tutorial(11)類別與物件

分享:

Kotlin Tutorial(10)函式 << 前情

封裝是物件導向程式設計一個基礎的特性,應用程式需要的資料與功能,通常會封裝為類別,使用類別建立的物件,可以大幅度簡化應用程式的設計,後續的修改與功能擴充,也都會簡單多了。

宣告應用程式需要的類別

一般的應用程式通常需要一些資料,例如下面這個瀏覽與管理記事資料的App,每一個記事資料可以儲存使用者輸入的標題與內容:

kt_11_01

對於應用程式來說,一個記事需要的資料,不應該是一些變數,而是一個封裝在類別裡面的屬性,每一個記事資料都是一個使用類別建立的物件。應用程式需要像這樣的資料,可以使用下面的語法宣告一個類別:

class 類別名稱 constructor(屬性變數宣告, ...)

屬性變數宣告的部份,根據這個類別需要的資料,使用下面的語法宣告不可以改變值的屬性變數,例如記事資料的編號:

val 屬性名稱: 屬性型態

使用下面的語法宣告可以改變值的屬性變數,例如記事資料的標題或內容:

var 屬性名稱: 屬性型態

宣告類別以後,就可以使用下面的語法,建立需要的物件,呼叫建構式建立物件的時候,必須根據類別的主要建構式宣告提供必要的參數:

val|var 變數名稱: 類別名稱 = 類別名稱(參數,...)

下面的範例程式宣告一個封裝記事的類別,類別名稱是Item01,每一個使用這個類別建立的物件,都會包含編號、標題與內容三個屬性變數:

/* net.macdidi5.kotlin.tutorial.ch11.Item01.kt */

package net.macdidi5.kotlin.tutorial.ch11

// 宣告包含id(編號)、title(標題)與content(內容)三個屬性的類別
// 為類別宣告主要建構式的參數,也同時宣告這個類別的屬性
class Item01 constructor (val id: Long,
                          var title: String,
                          var content: String)

fun main(args: Array<String>) {
    // 宣告建立Item01物件,依照主要建構式的順序提供參數
    val item01 = Item01(1, "Hello", "Hello Kotlin!")
    println("Item01(id=${item01.id}, title=${item01.title}, content=${item01.content})")
    // 顯示: Item01(id=1, title=Hello, content=Hello Kotlin!)

    // 編譯錯誤,使用val宣告的屬性不可以修改
    // item01.id = 11

    // 設定title與content
    item01.title = "Hello01"
    item01.content = "Hello Kotlin 01!"

    println("Item01(id=${item01.id}, title=${item01.title}, content=${item01.content})")
    // 顯示: Item01(id=1, title=Hello01, content=Hello Kotlin 01!)

}

為屬性變數設定預設值

使用宣告好的類別建立物件的時候,需要根據屬性的宣告,設定物件所有屬性的值。不過應用程式在建立物件的時候,可能只需要設定部份屬性的值。例如建立一個記事資料,只需要設定編號與標題。

如果希望在建立物件的時候,可以設定部份需要的屬性值,使用下面的語法,在類別的屬性變數宣告加入預設值的設定:

val 屬性名稱: 屬性型態 = 預設值
var 屬性名稱: 屬性型態 = 預設值

下面的範例程式為類別的屬性加入預設值的宣告,可以在建立物件的時候,依照不同的需求設定屬性值:

/* net.macdidi5.kotlin.tutorial.ch11.Item02.kt */

package net.macdidi5.kotlin.tutorial.ch11

// 宣告包含id(編號)、title(標題)與content(內容)三個屬性的類別
// 主要建構式的constructor關鍵字可以省略
// 為title與content兩個欄位設定預設值
class Item02 (val id: Long = 0,
              var title: String = "",
              var content: String = "") {
    // 宣告傳回資訊的函式
    fun getDetails() = "Item02(id=$id, title=$title, content=$content)"
}

fun main(args: Array<String>) {
    // 省略設定預設值的建構式參數title與content
    val item0201 = Item02(201)
    println(item0201.getDetails())
    // 顯示: Item02(id=201, title=, content=)

    // 省略設定預設值的建構式參數content
    val item0202 = Item02(202, "Hi")
    println(item0202.getDetails())
    // 顯示: Item02(id=202, title=Hi, content=)

    val item0203 = Item02(203, "Hi", "Hello")
    println(item0203.getDetails())
    // 顯示: Item02(id=203, title=Hi, content=Hello)
}

使用初始化區塊

為類別加入主要建構式以後,呼叫主要建構式建立的物件,根據提供的參數設定物件的 屬性值,如果需要檢查參數的正確性,或是根據屬性的應用執行額外的處理,可以在類別加入初始化區塊執行這類工作:

/* net.macdidi5.kotlin.tutorial.ch11.Item02.kt */

package net.macdidi5.kotlin.tutorial.ch11

// 宣告包含id(編號)、title(標題)與content(內容)三個屬性的類別
// 主要建構式的constructor關鍵字可以省略
// 為title與content兩個欄位設定預設值
class Item02 (val id: Long = 0,
              var title: String = "",
              var content: String = "") {

    // 初始化,可以在這裡檢查與處理屬性值
    init {
        title = if (title.isEmpty()) "Title required" else title
        content = if (content.isEmpty()) "Content required" else content
    }

    // 宣告傳回資訊的函式
    fun getDetails() = "Item02(id=$id, title=$title, content=$content)"
}

初始化區塊會在主要建構式接收參數並設定好屬性以後執行:

val item0203 = Item02(203, "Hi", "Hello")
println(item0203.getDetails())
// 顯示: Item02(id=203, title=Hi, content=Hello)

val item0204 = Item02(204, "", "")
println(item0204.getDetails())
// 顯示: Item02(id=204, title=Title required, content=Content required)

建立與使用物件陣列

需要處理多個物件的時候,如果個數是固定的,使用陣列是比較合適與建單的。下面的語法可以建立一個儲存多個物件的陣列:

var 名稱 = arrayOf(物件,...)

下面的範例程式示範使用陣列儲存與使用多個記事物件:

/* net.macdidi5.kotlin.tutorial.ch11.Item02.kt */

package net.macdidi5.kotlin.tutorial.ch11

// 宣告包含id(編號)、title(標題)與content(內容)三個屬性的類別
// 主要建構式的constructor關鍵字可以省略
// 為title與content兩個欄位設定預設值
class Item02 (val id: Long = 0,
              var title: String = "",
              var content: String = "") {
    fun getDetails() = "Item02(id=$id, title=$title, content=$content)"
}

fun main(args: Array<String>) {
    val items = arrayOf(
            Item02(2001),
            Item02(2002),
            Item02(2003)
    )

    // 使用迴圈處理陣列裡面的所有Item02物件
    for (item in items) {
        // 顯示Item02物件資訊
        println(item.getDetails())
    }
    // 顯示:
    // Item02(id=2001, title=, content=)
    // Item02(id=2002, title=, content=)
    // Item02(id=2003, title=, content=)

    println()

    for (item in items) {
        // 設定每一個元素Item02物件的title與content屬性
        item.title = "Hi"
        item.content = "Hello"
    }

    for (item in items) {
        println(item.getDetails())
    }
    // 顯示:
    // Item02(id=2001, title=Hi, content=Hello)
    // Item02(id=2002, title=Hi, content=Hello)
    // Item02(id=2003, title=Hi, content=Hello)
}

其它建構式

在宣告類別的時候,使用主要建構式與參數的預設值,就可以應付一般建立的需求。如果需要在建立物件的時候,執行一些額外工作,你也可以使用下面的語法,在類別加入其它建構式的宣告。其它建構式的宣告後面,一定要使用this(…)呼叫主要建構式:

constructor(參數名稱: 參數型態,...): this(...) {
    // 建構式執行的工作
}

下面的範例程式示範其它建構式的宣告與使用:

/* net.macdidi5.kotlin.tutorial.ch11.Item03.kt */

package net.macdidi5.kotlin.tutorial.ch11

class Item03 (val id: Long,
              var title: String,
              var content: String) {

    // 宣告次要建構式
    // 使用this呼叫主要建構式
    constructor(id: Long): this(id, "", "")

    fun getDetails() = "Item03(id=$id, title=$title, content=$content)"
}

fun main(args: Array<String>) {
    val item0301 = Item03(301, "Hello", "Hello Kotlin!")
    println(item0301.getDetails())
    // 顯示: Item03(id=301, title=Hello, content=Hello Kotlin!)

    val item0302 = Item03(302)
    println(item0302.getDetails())
    // 顯示: Item03(id=302, title=, content=)
}    

宣告屬性與自定存取函式

宣告類別的時候,可以在主要建構式同時宣告參數與類別需要的屬性。如果類別還需要其它額外的屬性,例如下面的範例程式:

/* net.macdidi5.kotlin.tutorial.ch11.Item04.kt */

package net.macdidi5.kotlin.tutorial.ch11

// 宣告包含id(編號)、title(標題)與content(內容)三個參數的建構式
// 因為宣告建構式參數沒有使用val或var,所以它們只是建構式的參數,並不是類別的屬性
class Item04(_id: Long,
             _title: String,
             _content: String) {
    // 宣告類別的屬性,並設定為建構式的參數
    val id = _id
    var title = _title
    var content = _content

    fun getDetails() = "Item03(id=$id, title=$title, content=$content)"
}

fun main(args: Array<String>) {
    val item04 = Item04(4, "Greeting", "Good morning")
    println(item04.getDetails())
    // 顯示: Item04(id=4, title=Greeting, content=Good morning)
}    

一般的應用不必使用上面範例程式的作法,不論是在主要建構式同時宣告參數與類別需要的屬性,或是另外在類別宣告需要的屬性,Kotlin都會自動為屬性加入預設的setter與getter函式,使用物件屬性值的時候,會自動轉換為呼叫setter與getter函式:

class Item(var id: Int)

fun main(args: Array<String>) {
    val item = Item(100)

    // 呼叫setId函式設定屬性值
    item.id = 101

    // 呼叫getId函式取得屬性值
    println(item.id)
}

如果需要在設定或讀取屬性值的時候,執行一些額外的工作,就必須在類別宣告屬性,使用set與get關鍵字,為屬性宣告自定的setter與getter函式:

/* net.macdidi5.kotlin.tutorial.ch11.Item05.kt */

package net.macdidi5.kotlin.tutorial.ch11

class Item05(val id: Long,
             _title: String,
             _content: String = "") {

    var title = _title
        // 宣告屬性的setter函式
        set(value: String) {
            if (value.isNotEmpty()) {
                // 使用field關鍵字取代屬性名稱title
                field = value
            }
        }

    var content = _content
        // 宣告屬性的getter函式
        get() {
            // 使用field關鍵字取代屬性名稱content
            return if (field.isEmpty()) "Empty" else field
        }

    fun getDetails() = "Item05(id=$id, title=$title, content=$content)"
}

fun main(args: Array<String>) {
    val item05 = Item05(5, "Nice day")
    println(item05.getDetails())
    // 顯示: Item05(id=5, title=Nice day, content=Empty)

    item05.title = ""
    println("title=${item05.title}")
    // 顯示: title=Nice day

    item05.content = ""
    println("content=${item05.content}")
    // 顯示: content=Empty

    //
    item05.content = "Nice day"
    println("content=${item05.content}")
    // 顯示: content=Nice day
}   

下一步

認識類別與物件基本的概念與設計以後,接下來準備瞭解物件導向技術中的繼承。設計功能比較複雜一些的應用程式,使用繼承可以簡化程式的撰寫,許多框架的應用,例如Android App,也都需要瞭解繼承的實作與特性。

相關的檔案都可以在GitHub瀏覽與下載。

http://github.com/macdidi5/Kotlin-Tutorial

後續 >> Kotlin Tutorial(12)繼承與函式覆寫

分享:
按讚!加入 CodeData Facebook 粉絲群

相關文章

留言

留言請先。還沒帳號註冊也可以使用FacebookGoogle+登錄留言

windmax106/25

您好!想請教一下次要建構式的使用時機??可以麻煩老師舉個例子嗎??看範例還是不曉得何時會用到@@"

熱門論壇文章

熱門技術文章