昨天,我們介紹了類別,而今天,我們則會討論它的第一個特性:繼承 (Inheritance)。
繼承的概念最少會涉及兩個類別,第一個是「被繼承」的父類別 (Super Class/Parent Class),而第二個則是「繼承」父類別的子類別 (Sub Class/Child Class)。而這裡的繼承關係的意思便是指子類別透過名為繼承的行為來使它擁有父類別中的變數與函式。這個特性在我們有多個相似的類別時,可以把它們重複的部分整合在一個父類別中來讓我們不用多次編寫重複的部分。不過,需要注意的是,雖然一個類別可以被無數個子類別繼承,但每個類別最多只能繼承一個父類別。
舉例來說,假如我們今天要為鴨子跟老鷹定義類別,我們發現它們有一些相同的部分,例如外表特徵的鳥喙、翅膀等跟行為的嗚叫等。因此,我們可以把這些相同的部分整合在一起定義一個父類別,鳥類,然後再讓鴨子類別跟老鷹類別去繼承鳥類類別,最後再在各自的類別中加上自己獨特的部分。以下是實作的例子:[C#]
using System;
// 父類別
public class Bird {
public string name; // 共同擁有的變數
public void speak() { // 共同擁有的函式
Console.WriteLine(name + " Quack!");
}
}
public class Duck : Bird { // 繼承 Bird 類別
public Duck() { // 建構子
name = "Duck"; // 存取並修改來自父類別的變數
}
public void swim() { // Duck 類別中獨特的函式
Console.WriteLine(name + " Swim!");
}
}
public class Eagle : Bird { // 繼承 Bird 類別
public Eagle() { // 建構子
name = "Eagle"; // 存取並修改來自父類別的變數
}
public void fly() { // Eagle 類別中獨特的函式
Console.WriteLine(name + " Fly!");
}
}
public class InheritanceExample
{
public static void Main(string[] args)
{
Duck duck = new Duck(); // 建立 Duck 物件
Eagle eagle = new Eagle(); // 建立 Eagle 物件
duck.speak(); // [使用父類別的函式] 顯示:Duck Quack!
duck.swim(); // [使用自己的函式] 顯示:Duck Swim!
eagle.speak(); // [使用父類別的函式] 顯示:Eagle Quack!
eagle.fly(); // [使用自己的函式] 顯示:Eagle Fly!
Bird bird = new Bird(); // 建立 Bird 物件
bird.name = "Bird";
bird.speak(); // [使用自己的函式] 顯示:Bird Fly!
bird.swim(); // 錯誤! 無法使用子類別的函式!
bird.fly(); // 錯誤! 無法使用子類別的函式!
}
}
從上面的例子中,我們可以看到:
1. 在父類別中定義的變數及函式可以在子類別中使用及修改
當我們在子類別中繼承了父類別後,它便已經擁有了父類別中的變數與函式,因此對程式來說,上面例子中的 Duck 類別和 Eagle 類別實際上是:[C#]
public class Duck {
public string name; // 來自 Bird 類別的變數
public void speak() { // 來自 Bird 類別的的函式
Console.WriteLine(name + " Quack!");
}
public Duck() { // 建構子
name = "Duck";
}
public void swim() {
Console.WriteLine(name + " Swim!");
}
}
public class Eagle {
public string name; // 來自 Bird 類別的變數
public void speak() { // 來自 Bird 類別的的函式
Console.WriteLine(name + " Quack!");
}
public Eagle() { // 建構子
name = "Eagle";
}
public void fly() {
Console.WriteLine(name + " Fly!");
}
}
不過,需要注意的是,並不是所有父類別的變數及函式都是在子類別繼承後便能使用,這是由於程式中有著保護層級 (Protection Level) 的概念,這使不同類別之間的變數及函式的存取加上了限制。不過,這部分我們會在後續再做更多的討論。
2. 在父類別中無法使用及修改在子類別中定義的變數及函式
繼承行為是單向的,我們可以在子類別中獲取父類別的資訊,但我們沒辦法在父類別中得知任何子類別的資訊。在上面的例子中,程式可以知道 Duck 類別和 Eagle 類別都是 Bird 類別的子類別,因此它們可以使用 Bird 類別的變數及函式。可是程式並不會知道 Bird 類別有什麼子類別,因此它不能使用 Duck 類別和 Eagle 類別中定義的變數及函式。
由此可見,透過繼承,我們可以在設計多個類別時把這些類別中相同的部分都整合在一起,只把不同的部分保留在各自的類別中。
最後,大家不妨可以思考一下,既然每一個類別都只能繼承一個父類別,那如果我們今天遇到一個狀況是不同的類別之間有不同的共通點,例如類別 A 跟類別 B 有相同的部分 A、類別 B 跟類別 C 有相同的部分 B、而類別 A 跟類別 C 又有相同的部分 C。在這樣的狀況下,我們又該如何做呢?
為了幫助大家想像,讓我們來看一個例子,假如現在我們要除了定義鴨子跟老鷹的類別外,還要定義玩具飛機跟玩具輪船的類別:
老鷹類別(Eagle):[變數] 名稱(name) [函式] 鳴叫(speak) 飛行(fly)
鴨子類別(Duck):[變數] 名稱(name) [函式] 鳴叫(speak) 游泳(swim)
玩具飛機類別(ToyPlane):[變數] 價格(price) [函式] 充電(charge) 飛行(fly)
玩具輪船類別(ToyShip):[變數] 價格(price) [函式] 充電(charge) 游泳(swim)
我們可以看到每個類別都會跟至少兩個不同的類別有著相同的變數或函式,那我們該怎麼做呢?今天就讓大家先思考一下,讓我們明天繼續討論吧~