物件導向的繼承特性用來定義類別之間的層級關係。
建立子類別 (Subclass)
一但子類別繼承某個類別,就會共用該類別的全部屬性。子類別繼承的類別,通常稱作父類別(parent class)或是超類別 (Superclass)
open 關鍵字
將類別加上 open
關鍵字,以開放給別的類別繼承。
open class Room(val name:String){
fun description() = "Room: $name" fun load() = "Nothing much to see here..."
}
繼承
在子類別名稱後方利用冒號加上父類別。
class TownSquare: Room("Town Square")
- 如此,TownSpuare 就會繼承 Room,但是兩個類別內容完全一樣(只有名稱不一樣)
覆寫 (Overridling)
子類別可以覆蓋屬性和行為,或者自己實作特有的屬性與行為。
class TownSquare: Room("Town Square"){
override fun load() = "The villagers rally and cheer as your enter!"
}
- 被覆寫父類別的函數需要加上
open
關鍵字。
open class Room(val name:String){
fun description() = "Room: $name" open fun load() = "Nothing much to see here..."
}
super 關鍵字
繼承父類別後,可以使用 super
參考父類別的屬性或函數。
範例:父類別加上 protected open val dangerLevel = 5
open class Room(val name:String){
protected open val dangerLevel = 5 fun description() = "Room: $name\\n" +
"Danger level: $dangerLevel" open fun load() = "Nothing much to see here..."
}
- 在子類別覆寫,並使用 super 參考父類別的 dangerLevel 值
class TownSquare: Room("Town Square"){
override val dangerLevel = super.dangerLevel - 3 override fun load() = "The villagers rally and cheer as your enter!"
}
子類別增加自訂屬性和函數
class TownSquare: Room("Town Square"){
override val dangerLevel = super.dangerLevel - 3
private var bellSound = "GWONG" override fun load() = "The villagers rally and cheer as your enter!\\n${ringBell()}" private fun ringBell() = "The bell tower announces your arrival. $bellSound"
}
呼叫子類別的函數
fun main(){
val currentRoom: Room = TownSquare()
println(currentRoom.description())
println(currentRoom.load())
}
//The villagers rally and cheer as your enter!
//The bell tower announces your arrival. GWONG
多型 (Polymorphism)
在上面的範例中,currentRoom 宣告的型別是 Room,但是產生的實體為 TownSquare 類別。因為 TownSquare 是 Room 的子類別,所以這樣宣告是可以的。
同理,若有另外一個類別繼承 TownSquare,也可以給 currentRoom 使用。
這就是多型,多型的設計特性能夠簡化程式碼結構。透過多型,便可在一組類別裡複用個性化函數。
final 關鍵字
每一個類別的屬性及函數都能夠被複寫,若不想被複寫,可以使用 final 關鍵字修飾符。
class TownSquare: Room("Town Square"){
override val dangerLevel = super.dangerLevel - 3
private var bellSound = "GWONG" final override fun load() = "The villagers rally and cheer as your enter!\\n${ringBell()}" private fun ringBell() = "The bell tower announces your arrival. $bellSound"
}
- 現在任何 TownSquare 的子類別都只能複寫 description 函數,更動不了 load 函數。
類型檢測 (Type checking)
到目前為止,我們建立了一個父類別 Room,並由 TowerSquare 繼承它,繼承關係圖如下:
is 運算子
將 is 運算子左邊物件的類型和右邊的類型相比較,相同則回傳 true,反之回傳 false。
var room = Room("Foyer")
var townSquare = TownSquare()room is Room //trueroom is TownSquare //falsetownSquare is TownSquare // true
townSquare is Room // true
Any 類
在 Kotlin 中,所有的類別都是繼承 Any 類,所以 TowerSquare 的繼承關係圖,應改為:
var room = Room("Foyer")
var townSquare = TownSquare()room is Any //truetownSquare is Any // true
型別轉換
fun printIsSourceOfBlessings(any:Any){
val isSourceOfBlessings = if (any is Player){
any.isBlessed
}else{
(any as Room).name == "Fount of Blessing"
}
print("$any is a source of blessings: $isSourceOfBlessings")
}
- 上面範例中,函數 printIsSourceOfBlessings 傳入引數的型別為 Any,也就是說任意的型別的值都可以傳進來。
- if 運算式用
is
運算子判斷 any 是否為 Player 類,如果成立,表示 any 是 Player 類,所以就可以直接讓 any 使用 Player 的屬性,這稱為「智慧型別轉換」。反之,若不成立,表示 any 不是 Player 類,那是什麼類呢?編譯器不知道,所以不能夠參考其他類的屬性。利用as
運算子將傳入的引數 any (型別為 Any) 轉換為 Room。
小結
- 每一個類別在 Kotlin 中,預設都是不能被繼承的,必須要將類別加上關鍵字
open
,才允許子類別繼承。這一點與 Java 有很大的不同 (在 Java 每一個類別預設都能夠被繼承,除非加上關鍵字 final。) - 要繼承父類別的函數,除了父類別需要加上
open
關鍵字外,還必須也在函數加上open
,才能夠開放給其他類別繼承。 - 所有的類別都有隱性繼承
Any
類別。 - 運算子 is 用來檢查型別;運算子 as 用來做類型轉換。
如果本篇文章有幫助到您,請 👏🏻鼓勵我。
謝謝 🙇🏻