菜雞與物件導向 (6): 抽象、覆寫
Last updated
Last updated
備註:這邊的抽象是指程式語言中的抽象類別,而非抽象化
抽象的概念很直接,請回想一下前面的例子就可以了:
當我們在用卡牌的例子時,雖然怪獸卡跟魔法卡都繼承了 Card 這個類別,但是我們仍然能 new Card()
來建立一張新卡牌,那…怪怪的吧,這張卡牌到底是什麼呀,空白的卡片嗎?
又或是動物的例子,我們的狗跟貓都繼承了哺乳類,那我們能實例化一個哺乳類嗎?我們的狗跟鳥都是動物,那我們能實例化一個動物嗎?
小明跟小華都繼承了工程師,那我們能 new 一個工程師嗎…?
有些類別就是這樣,它們負責定義共通的那些特性,然而它們本身不應該被實體化成一個物件,這種類別我們就應該把它們標記為抽象類別。
抽象類別在 C# 裡用 abstract
這個修飾詞來表示,可以加在類別或方法上。例如 abstract class Animal
就代表動物這個類別是個抽象類別,它不能被實例化。
而當加在方法上時,例如 public abstract void Eat()
就是代表這個進食的方法無法被叫用,只能由繼承者去重新定義這個方法。
那麼繼承者們,也就是衍生類別如何去重新定義父類別的方法呢?
所謂「欲戴王冠,必 Override」,這時候就必須使用覆寫(override
)。
覆寫是指對於像是前述的抽象方法時,在同名的方法前加上 override
關鍵字就可以讓程式知道你要覆寫這個方法(你不覆寫的話,編譯器還會生氣)。
例如前述的 Eat,狗就可以用 public override void Eat()
的方式去覆寫吃東西這個方法:
但有時候我們只是希望秉持著多型的精神,讓子類別有可以重新定義的彈性,這時候我們就會使用 虛擬(virtual
) 的方式去標記這個方法,如此一來就可以實作,同時也讓子類別可以覆寫。
例如可能狗有 public virtual void Eat()
這個進食的方法:
那假設我們有個 Giwawa
繼承了 Dog
,但牠也是吃熱狗的,就可以選擇不去覆寫 Eat()
:
而當我們有了 RobotDog
這個類別,它就可以繼承並且重新改寫掉 Eat()
這個方法,從吃肉變成喝汽油。
除了使用 override
去覆寫父類別的方法以外,也可以用 new
去隱藏父類別的方法:
override
和 new
的差別在於多型時轉型成父類別時的行為:
override
會直接取代掉父類別的方法,即使轉型為父類別還是以子類別的實作為主
new
則是會建立一個子類別專屬的方法,若轉型為父類別就會變回父類別的方法
我們直接用例子來看看吧,假設我們現在有「拉不拉多」和「機器狗」,都繼承了「狗」。差別在於拉不拉多 override 了 Eat()
這個方法,而機器狗 new 了 Eat()
這個方法:
接著讓我們來看看當他們被實例化之後,以及被轉型為父類別的時候的 Eat()
有什麼不一樣吧:
可以看到使用 new
來覆寫的 RobotDog 在被轉型為 Dog 的時候突然就變回吃熱狗了!
要特別注意的是:當你覆寫了父類別的方法,卻忘記加上 override
的話,默認會當成是要 new
,所以覆寫的時候還是小心一點,具體地把 override
或 new
寫出來吧!
關於抽象和覆寫這部份的範例,因為我個人碰觸的比較少,唯恐我的舉例不夠深入,這邊再附上幾個不錯的範例,可以作為參考:
接著下一篇,我們就接著看這一部分的最後一片拼圖:介面吧!
本系列下一篇:菜雞與物件導向 (7): 介面