繼承

      在〈繼承〉中尚無留言

類別繼承

繼承是什麼? 為什麼要有繼承? 

假設我們有二個類別,分別為 class Pikachu(皮卡丘) 及 class Eve(依布),而這二個類別都同樣有 level的物件變數,且這二個類別都是神奇寶貝  “Pokemon”。

那麼我們就可以把共通的屬性 level置於父類別Pokemon,然後Pikachu及Eve繼承Pokemon。這樣就可以直接copy Pokemon 的 level屬性。

fun main(args:Array<String>){
var p1=Pikachu()
p1.level=100
var e1=Eve()
e1.level=10
println("p1 level : ${p1.level}")
println("e1 level : ${e1.level}")
}
open class Pokemon{
var level:Int=0
constructor(){
println("神奇寶貝出生了")
}
}
class Pikachu: Pokemon{
constructor(){
println("皮卡丘出生了")
}
}
class Eve: Pokemon{
constructor(){
println("Eve出生了")
}
}
結果:
神奇寶貝出生了
皮卡丘出生了
神奇寶貝出生了
Eve出生了
p1 level : 100
e1 level : 10

Pikachu及Eve二個子類別,並沒有宣告level這個物件變數,但卻可以在p1及e1取得level,這就表示Pikachu及Eve這二個類別 copy了Pokemon裏的level變數。

class 預設為final

kotlin的類別預設為final,是不准被繼承的。所以為了要能繼承Pokemon, 就必需父類別Pokemon之前加上open 關鍵字,才可被Pikachu繼承。

繼承運算子 “:”

kotlin繼承使用 “:” 運算子,跟Java的 “extends” 是同樣的意思。

class Pikachu : Pokemon{}

Any類別

類別若沒有繼承的父類別,則系統會自動加入繼承Any類別。也就是說,Any是所有類別的父類別。只有Any沒有老爸,他是從石頭蹦出來的。

class Pokemon:Any{//系統自動加入Any
}

建構子

最簡單的繼承如下,子類別一定要有建構子。如此才有機會在後面加入 :super() 調用父類別建構子

fun main(args:Array<String>){
var p1=Pikachu()
}
open class Pokemon{}
class Pikachu:Pokemon{
constructor(){//在此系統會自動加入:super()
}
}

除了使用次建構子外,也可以在繼承中指定父類別建構子的進入點

open class Pokemon{}
class Pikachu:Pokemon(){}

如果子類別有主建構子,那繼承時就必需手動指定要調用的父類別建構子

open class Pokemon(a:Int){}

class Pikachu(var level:Int):Pokemon(10){}

如果子類別有主建構子及次建構子,那次建構子一定要手動加入 :this()調用主建構子,順序如下
1. 次建構子藉由 :this() 調用主建構子
2. 主建構子調用父類別建構子

open class Pokemon(a:Int){}

class Pikachu(var level:Int):Pokemon(10){
constructor():this(1){}
}

建構子覆蓋

子類別建構子覆蓋父類別建構子時,父類別建構子不需要啟用 “open” 關鍵字。因為建構子預設就是open,而且也不容許手動設為final。

但請注意,建構子是可以設定為private的,如 private constructor():this(1{}

open class Pokemon(a:Int){
constructor():this(1){
println("神奇寶貝出了")
}
}

class Pikachu(var level:Int):Pokemon(){
constructor():this(1){
println("皮卡丘出生了")
}
}
結果:
神奇寶貝出了
皮卡丘出生了

物件變數覆蓋(override)

如果父子類別的物件變數同名,則子類別物件變數就會覆蓋父類別物件變數。不過父類別物件變數預設為final,是不可被覆蓋的。所以父類別的物件變數必需加上open,子類別物件變數必需加上override。如下所示

fun main(args:Array<String>){
var p1=Pikachu(10)
}
open class Pokemon (open var level:Int){
constructor():this(10){
println("父類別建構子")
}
}
class Pikachu(override var level:Int): Pokemon(level){
}

方法覆蓋

子類別覆蓋父類別方法時,需注意父類別方法預設是final的,所以需在父類別方法前加上open,然後於子類別方法前再加入 override。

複習一下,方法預設是final,但建構子預設是open,而且不能更改成final。

fun main(args:Array<String>){
var p1=Pikachu()
p1.eat()
}
open class Pokemon{
var level:Int=0
open fun eat(){
println("素食的")
}
}
class Pikachu: Pokemon(){
override fun eat(){
println("雜食的")
}
}
結果:
雜食的

多型

子類別物件可以使用父類別參考,如下產生一個p2 的Pikachu,但他的型態卻是Pokemon。p2此時就無法使用子類別的擴充功能(lightning),但可借由 p2 as Pikachu 進行強制轉型而取出lightning的功能。

不過e1是Eve物件,也是使用Pokemon參考,此時e1也可由 e1 as Pikachu 強制轉型成Pikachu。這種現像稱為 “爛梨假蘋果”。經由偽裝後的e1可以欺騙編譯器取得 lightning的擴充功能。不過在執行
(e1 as Pikachu).lightning() ,會產生runtime error。所以在轉型前,最後好 if (e1 is Pikachu)先判斷一次再轉型。

fun main(args:Array<String>){
var p1=Pikachu()
p1.lightning()
var p2:Pokemon=Pikachu()
(p2 as Pikachu).lightning()
var e1:Pokemon=Eve()
if(e1 is Pikachu)
(e1 as Pikachu).lightning()
}
open class Pokemon{
var level:Int=0
}
class Pikachu: Pokemon(){
fun lightning(){
println("十萬伏特閃電攻擊")
}
}
class Eve: Pokemon(){}

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *