這個問題記得面試時碰過兩. 三次,要你說出介面跟抽象類別兩者之間的差異,我只知道其中相似之處就是要讓子類別來實作其內容,其他也說不出來有什麼相異之處。直到最近工作常會接觸到介面,這個問題又被我想起來了。
在開始前先解說一下名詞,interfcae跟abstract類別都是在替子類別擴充一些功能。有些會有強制性,但有些沒有。強制有兩種做法,分別為:
1.abstract的強制行為稱為:override(覆寫)
2.interface的強制行為稱為:implement(實作)。另外補充,在java裡面,類別繼承是使用extent,介面繼承則是使用implement)
- 抽象類別(abstract class):
public abstract class Car
{
public abstract string Run();//abstract method一定要寫在abstract class裡面,class沒有加abstract的話會報錯
public virtual string Navigation() { return "紙本地圖導航"; }
}
AutoMobile繼承Car,由於Car類別有一個abstract method,所以AutoMobile一定要覆寫Run()。不管是abstract或是virtual,要覆寫時,都要加上override關鍵字
public class AutoMobile: Car
{
public override string Run()
{
return "時速可達100公里";
}
public override string Navigation()
{
//return base.Navigation();
return "使用CarPlay 導航功能";
}
}
沒有覆寫Run()的話,可是會出現錯誤訊息的
virtual method則不像abstract method一樣,需要強制覆寫。由此可知抽象類別內的成員如果沒有abstract的關鍵字的話,是沒有強制性的!
public class Bicycle: Car
{
public override string Run()
{
return "時速可達30公里";
}
}
執行Program.cs看看結果
var autoMobile = new AutoMobile();
Console.WriteLine(autoMobile.Run());
Console.WriteLine(autoMobile.Navigation());
var bicycle = new Bicycle();
Console.WriteLine(bicycle.Run());
Console.WriteLine(bicycle.Navigation());
Console.ReadLine();
接著再增加一個MotorCycle類別,但Navigation方法不加上override關鍵字,來看看會出現什麼
public class MotorCycle : Car
{
public override string Run()
{
return "時速可達70公里";
}
public string Navigation()
{
return "使用手機GoogleMap導航功能";
}
}
Program.cs
這裡我們以多型的方式來建立一個Car物件,並宣告為MotorCycle型別(何謂多型可參考:[C#]多型(Polymorphism)一個簡單的範例)
省略...
Car motorCycle = new MotorCycle();//以多型宣告
Console.WriteLine(motorCycle.Run());
Console.WriteLine(motorCycle.Navigation());
Console.ReadLine();
結果如下,如果沒有加上override關鍵字的話,會執行父類別的Navigation(),因為motorCycle
變數他就是一個Car物件,所以會執行父類別的方法。
我們把MotorCycle的Navigation加上override,再來看看執行結果
public override string Navigation()
{
return "使用手機GoogleMap導航功能";
}
加上override後就可以執行被覆寫的Navigation方法了
綜合以上結果,可以得知:
1.抽象(abstract)類別是客製化不同類別之間的共同項目,並由子類別自行覆寫,藉此子類別擴充功能。
2.抽象類別裡面的東西,不一定要強制複寫(但有加abstract關鍵字的就一定要強制覆寫)
- 介面(Interface):
interface的方法無法實作,只提供interface成員定義。不像abstract class的method也可以有自己的實作內容。
這樣說可能有點抽象,我們先來建立一個interface。與Car類別不同的是,我們新增了一個Reverse()的倒車方法,並且讓MotorCycle類別來實作看看。
public interface ICar
{
public string Run();
public string Navigation();
public string Reverse();//倒車
}
interface不需要override關鍵字即可進行method的實作,所以我們把Run()的override拿掉。
public class MotorCycle : ICar
{
public string Run()
{
return "時速可達70公里";
}
public string Navigation()
{
return "使用手機GoogleMap導航功能";
}
}
這時會出現錯誤訊息,告訴我們沒有實作到Reverse這個方法。由此可以得知,與abstract類別不同的是,interface的所有的成員都是有強制性的,只要有繼承介面的話,都一定要實作其內容
接下來我們在來做個測試,如果MotorCycle類別同時繼承abstract跟interface的話,其成員覆寫(實作),會以誰為主?
先拿掉Navigation()來看看會發生什麼事。Navigation()在Car類別裡為virtual方法(不強制覆寫),但在ICar裡我們有定義一個Navigation(),但是剛剛有提到介面有強制性。在這樣的情形下會不會報錯呢?
public class MotorCycle : Car, ICar
{
public override string Run()
{
return "時速可達70公里";
}
//public string Navigation()
//{
// return "使用手機GoogleMap導航功能";
//}
public string Reverse()
{
return "後退";
}
}
結果是沒有任何錯誤訊息
這時的你可能會搞混,為什麼繼承了ICar,但有沒有實作Navigation,卻不會有任何錯誤訊息,我們來看以下的執行結果,就可以知道答案了。
原來是MotorCycle類別的父類別已經有Navigation()了,所以子類別繼承了父類別的Navigation方法,所以沒有出現MotorCycle未實作介面成員Navigation的錯誤訊息
綜合以上結果,可以得知:
1.介面先定義出不同子類別之間共有功能的定義。並由子類別自行實作,藉此擴充子類別功能。
2.介面與抽象類別不同,所有成員接必須強制進行實作
abstract(抽象類別) | interface(介面) | |
強制性 | 不一定有強制性的,有加abstract的method必須要強制覆寫 | interface的實作有強制性 |
繼承 | 子類別只能繼承一個抽象類別 | 子類別可以繼承多個介面 |
實例 | 不能被實例,專門用來被繼承 | 不能直接實例,要透過子類別實例 |
什麼時候使用interface? 什麼時候使用abstract?
以我們上面的Car範例來說,車子共有的功能,如前進. 後退. 煞車我們可以使用interface來強制制定相關車子必有的功能。至於非必要功能則可以用abstract讓子類別自行來覆寫,例如坦克車一定有其他車子也有的前進. 後退. 煞車功能,但其他車子類別一定不會(?)有坦克的發射砲彈功能,所以我們就能把發射砲彈這個功能放到abstract讓需要此功能的子類別(如:坦克車. 裝甲車)來自行繼承覆寫。
以下為擷取別人文章的內容,也是在講述什麼時候使用interface,什麼時候該使用abstract。大家也可以參考看看喔!
若把焦點放在方法實作上,可能會感到有點迷思,因為兩者好像都可以被覆寫。筆者也找到一篇算淺顯易懂的文章(參4),該文作者以門為例,當我們在建立類別時,一般門可能會有開門跟關門,假如有天要增加一個新的功能,比如自動門、設定密碼,作法一可以直接加入abstract或interface中,但這樣會面臨到耦合度的問題。當要建立木門所有功能,明明木門不能設定密碼,但因為父層的interface或abstract有設定密碼的功能,導致木門也得到這個功能了,再把問題換成公司系統,不同職位的權限或可執行的功能一定不同,若出現這樣的狀況是非常不合理的事情。看完了他的例子應該會更有感覺,abstract class仍屬於class,故僅能單一繼承,而繼承interface則可以多重繼承,所以門為例,屬於名詞的共通特性,應該放在abstract,而屬於其他覆加的功能,則可以放在interface,透過interface可多重實現的特性,工程師就可以自行組合成各式各樣的門,那今天就介紹到這裡啦!
ref:
0.如何在 C# 程式設計手冊 () 定義抽象屬性
1.什麼時候使用interface? 什麼時候使用abstract?
2.C#雜記 — 介面(interface)、抽象( abstract)、虛擬(virtual)之我見 | by 莊創偉 |
3.C# 繼承(Inheritance)(Y)