Kotlin Tutorial(11)類別與物件 << 前情
如果應用程式的功能比較多一些,或是架構比較複雜一些,開發人員通常會使用物件導向的繼承,可以使用更精簡的程式碼,完成應用程式需要的功能。開發Android App元件的時候,也會繼承Android SDK提供的類別,再完成元件需要的工作。所以瞭解繼承與函式覆寫,幾乎是開發各種應用程式必要的特性。
沒有使用繼承設計的類別
設計瀏覽與管理記事資料的App,提供使用者儲存基本的標題與內容:

為了讓App可以提供更多功能,讓使用者可以另外儲存照片的記事資料:
依照基本的封裝特性,你會宣告一個像這樣的基本記事類別:
/* net.macdidi5.kotlin.tutorial.ch12.stage01.Item.kt */
package net.macdidi5.kotlin.tutorial.ch12.stage01
// 宣告封裝項目的類別
// 包含編號、標題與內容三個屬性
class Item (val id: Long,
var title: String,
var content: String)
可以儲存照片的記事類別,依照同樣的封裝概念,你會宣告這樣的類別:
/* net.macdidi5.kotlin.tutorial.ch12.stage01.ImageItem.kt */
package net.macdidi5.kotlin.tutorial.ch12.stage01
// 宣告封裝可以儲存照片項目的類別
// 包含編號、標題、內容與照片檔名四個屬性
class ImageItem(val id: Long,
var title: String,
var content: String,
var imageFile: String)
依照使用者的選擇,在App運作的時候,使用上面宣告的類別建立需要的物件:
/* net.macdidi5.kotlin.tutorial.ch12.stage01.Demo01.kt */
package net.macdidi5.kotlin.tutorial.ch12.stage01
fun main(args: Array<String>) {
val i = Item(1, "Hello", "Hello Kotlin!")
println("id=${i.id}, title=${i.title}, content=${i.content}")
val i02 = ImageItem(2, "Hi", "Hello", "kotlin.jpg")
println("id=${i02.id}, title=${i02.title}, " +
"content=${i02.content}, imageFile=${i02.imageFile}")
}
使用繼承設計的類別
你可以發現上面的作法是很不好的,一般記事類別Item與可以儲存照片的ImageItem類別,有重複的屬性宣告,如果需要變更的時候,例如為記事資料加入日期與時間,那所有類別都需要修改。
所以在設計應用程式的時候,為了避免重複的宣告,應該使用繼承的特性來設計這些類別。應用程式已經宣告好一般記事類別Item,在宣告可以儲存照片的ImageItem類別之前,先把Item類別定義為可以被繼承的父類別(super class),宣告類別的時候沒有使用open關鍵字,表示不可以被繼承:
/* net.macdidi5.kotlin.tutorial.ch12.stage02.Item.kt */
package net.macdidi5.kotlin.tutorial.ch12.stage02
// 在宣告項目類別的時候,使用open關鍵字讓它可以被繼承
open class Item (val id: Long,
var title: String,
var content: String)
下面是宣告子類別的基本語法:
class 類別名稱(參數名稱: 參數型態,..., val|var 屬性名稱: 屬性型態): 父類別名稱(參數,...)
接下來在宣告可以儲存照片的ImageItem類別,使用上面的語法讓它繼承Item類別:
/* net.macdidi5.kotlin.tutorial.ch12.stage02.ImageItem.kt */
package net.macdidi5.kotlin.tutorial.ch12.stage02
// 在宣告主要建構式的時候
// 從父類別繼承的id、title與content屬性
// 不使用val或var宣告,所以它們只是參數
// 另外使用var宣告ImageItem需要的imageFile屬性
// 在類別宣告的最後面
// 使用Item(id, title, content)呼叫父類別Item的主要建構式
// 設定編號、標題與內容
class ImageItem(id: Long,
title: String,
content: String,
var imageFile: String): Item(id, title, content)
使用繼承設計的類別也可以依照使用者的選擇,在App運作的時候建立需要的物件:
/* net.macdidi5.kotlin.tutorial.ch12.stage02.Demo02.kt */
package net.macdidi5.kotlin.tutorial.ch12.stage02
fun main(args: Array<String>) {
val i = Item(1, "Hello", "Hello Kotlin!")
println("id=${i.id}, title=${i.title}, content=${i.content}")
// 建立ImageItem物件後,包含從父類別Item繼承的id、title與content
val i02 = ImageItem(2, "Hi", "Hello", "kotlin.jpg")
println("id=${i02.id}, title=${i02.title}, " +
"content=${i02.content}, imageFile=${i02.imageFile}")
}
函式覆寫
父類別宣告的屬性與函式,都會被子類別繼承,子類別繼承自父類別的函式,會依照函式的工作,可能需要在子類別執行覆寫(Overriding Methods)的工作。在宣告父類別函式的時候,加入open關鍵字讓子類別可以覆寫這個函式:
/* net.macdidi5.kotlin.tutorial.ch12.stage03.Item.kt */
package net.macdidi5.kotlin.tutorial.ch12.stage03
open class Item (val id: Long,
var title: String,
var content: String) {
// 傳回簡短的記事內容
// 因為不需要讓子類別覆寫,所有不需要加入open關鍵字
fun getReduceContent(length: Int = 5) =
"${content.substring(0 until length)}..."
// 加入open關鍵字讓子類別可以覆寫這個函式
// 因為子類別需要加入額外的資訊,例如ImageItem的imgaeFile
open fun getDetails() = "id=$id, title=$title, content=$content"
}
宣告子類別的時候,加入override關鍵字覆寫父類別的函式:
/* net.macdidi5.kotlin.tutorial.ch12.stage03.ImageItem.kt */
package net.macdidi5.kotlin.tutorial.ch12.stage03
class ImageItem(id: Long,
title: String,
content: String,
var imageFile: String): Item(id, title, content){
// 不用覆寫繼承自父類別的getReduceContent函式
// 加入override關鍵字覆寫父類別的函式
// 顯示繼承自父類別的id、title與content
// 還有自己宣告的imageFile
override fun getDetails() = "id=$id, title=$title, " +
"content=$content, imageFile=$imageFile"
}
ImageItem子類別不用宣告父類別已經宣告好的函式,只要針對需要覆寫的函式實作就可以了:
/* net.macdidi5.kotlin.tutorial.ch12.stage03.Demo03.kt */
package net.macdidi5.kotlin.tutorial.ch12.stage03
fun main(args: Array<String>) {
val i = Item(1, "Hello", "Hello Kotlin!")
// 顯示呼叫函式傳回的項目資訊
println(i.getDetails())
// 顯示簡短的記事內容(預設為五個字)
println(i.getReduceContent())
val i02 = ImageItem(2, "Hi", "Good morning", "kotlin.jpg")
// 顯示呼叫函式傳回的項目資訊,這是ImageItem自己寫的函式
println(i02.getDetails())
// 顯示四個字的簡短記事內容,這是繼承自父類別Item的函式
println(i02.getReduceContent(4))
}
在覆寫的函式中呼叫父類別的函式
比對上面實作的程式碼,應該很容易發現不太好的作法:
// Item類別的函式
open fun getDetails() = "id=$id, title=$title, content=$content"
// ImageItem類別的函式,包含父類別getDetails函式的程式碼
override fun getDetails() = "id=$id, title=$title, " +
"content=$content, imageFile=$imageFile"
這樣的作法就違反繼承重要的特性:避免重複的程式碼。所以在子類別覆寫函式的時候,通常會使用super關鍵字呼叫父類別的函式:
/* net.macdidi5.kotlin.tutorial.ch12.stage04.ImageItem.kt */
package net.macdidi5.kotlin.tutorial.ch12.stage04
class ImageItem(id: Long,
title: String,
content: String,
var imageFile: String): Item(id, title, content){
// 使用super關鍵字呼叫父類別的函式
override fun getDetails() = "${super.getDetails()}, imageFile=$imageFile"
}
下一步
瞭解繼承基本的特性與作法以後,接下來準備認識物件導向基礎中的最後一個多型的特性,不論是自己設計應用程式,或是使用各種架構的API,都需要瞭解多型的特性,才可以使用精簡的程式碼完成應用程式的設計。
相關的檔案都可以在GitHub瀏覽與下載。
http://github.com/macdidi5/Kotlin-Tutorial
後續 >> Kotlin Tutorial(13)多型的特性與應用
|
林緯
04/12
謝謝分享,收穫良多~