前言
很多新人對這個問題已經看到的厭倦或者是噁心了,有可能是因為比較難理解或者是未理解的情況下對它們的認知不夠所以產生的想法是差不多,不用抽獎類和介面我一樣能實現我想要的功能。
這句話確實不假,但是實現的功能是否符合物件導向思想就不得而知了,也有很多的朋友知道它們的意思但是許久不用也忘記的差不多了,那麼我們今天就在來鞏固下 抽象類和介面的一些區別。
為什麼頻頻出現這個問題,無論是在面試過程中還是面試題上很多公司都在問這個問題,考驗的無非幾點1:對物件導向的理解 2:基本功紮實的程度,為什麼這個說呢?介面和抽象類他們在很多情況下會讓使用者混淆,比如:我們建立了一個介面裡面填寫上了定義的方法但是介面需要被子類繼承和子類實現具體方法(這常用的我就不說了),再有抽象類抽象類中定義了抽象方法也需要被子類繼承並且實現。很多朋友在學習的過程中看到這兩點覺得差不多啊再加上用的少所以混淆了一些,我們可以理解兩者的“差不多”甚至可以理解介面是變相的輕量級抽象類因為他們確實有很多共同之處,好了不廢話了舉例說明!
一、抽象類和介面有什麼區別
宣告方法的存在而不去實現它的類被叫做抽象類(abstract class),它用於要建立一個體現某些基本行為的類,併為該類宣告方法,但不能在該類中實現該類的情況。不能建立abstract 類的例項。然而可以建立一個變數,其型別是一個抽象類,並讓它指向具體子類的一個例項。不能有抽象建構函式或抽象靜態方法。Abstract 類的子類為它們父類中的所有抽象方法提供實現,否則它們也是抽象類為。取而代之,在子類中實現該方法。知道其行為的其它類可以在類中實現這些方法。
介面(interface)是抽象類的變體。在介面中,所有方法都是抽象的。多繼承性可通過實現這樣的介面而獲得。介面中的所有方法都是抽象的,沒有一個有程式體。介面只可以定義static final成員變數。介面的實現與子類相似,除了該實現類不能從介面定義中繼承行為。當類實現特殊介面時,它定義(即將程式體給予)所有這種介面的方法。然後,它可以在實現了該介面的類的任何物件上呼叫介面的方法。由於有抽象類,它允許使用介面名作為引用變數的型別。通常的動態聯編將生效。引用可以轉換到介面型別或從介面型別轉換,instanceof 運算子可以用來決定某物件的類是否實現了介面。
1、抽象類
(1) 抽象方法只作宣告,而不包含實現,可以看成是沒有實現體的虛方法
(2) 抽象類不能被例項化
(3) 抽象類可以但不是必須有抽象屬性和抽象方法,但是一旦有了抽象方法,就一定要把這個類宣告為抽象類
(4) 具體派生類必須覆蓋基類的抽象方法
(5) 抽象派生類可以覆蓋基類的抽象方法,也可以不覆蓋。如果不覆蓋,則其具體派生類必須覆蓋它們。如:
public abstract class A //抽象類A { private int num=0; public int Num //抽象類包含屬性 { get { return num; } set { num = value; } } public virtual int getNum() //抽象類包含虛方法 { return num; } public void setNum(int n) // //抽象類包含普通方法 { this.num = n; } public abstract void E(); //類A中的抽象方法E } public abstract class B : A //由於類B繼承了類A中的抽象方法E,所以類B也變成了抽象類 { } public class C : B { public override void E() //重寫從類A繼承的抽象方法。如果類B自己還定義了抽象方法,也必須重寫 { //throw new Exception("The method or operation is not implemented."); } } public class Test { static void Main() { C c = new C(); c.E(); } }
2、接 口
(1) 介面不能被例項化
(2) 介面只能包含方法宣告
(3) 介面的成員包括方法、屬性、索引器、事件
(4) 介面中不能包含常量、欄位(域)、建構函式、解構函式、靜態成員。如:
public delegate void EventHandler(object sender, Event e); public interface ITest { //int x = 0; int A { get; set; } void Test(); event EventHandler Event; int this[int index] { get; set; } }
(5) 介面中的所有成員預設為public,因此介面中不能有private修飾符
(6) 派生類必須實現介面的所有成員
(7) 一個類可以直接實現多個介面,介面之間用逗號隔開
(8) 一個介面可以有多個父介面,實現該介面的類必須實現所有父介面中的所有成
3、抽象類和介面
相同點:
(1) 都可以被繼承
(2) 都不能被例項化
(3) 都可以包含方法宣告
(4) 派生類必須實現未實現的方法
區 別:
(1) 抽象基類可以定義欄位、屬性、方法實現。介面只能定義屬性、索引器、事件、和方法宣告,不能包含欄位。
(2) 抽象類是一個不完整的類,需要進一步細化,而介面是一個行為規範。微軟的自定義介面總是後帶able欄位,證明其是表述一類“我能做。。。”
(3) 介面可以被多重實現,抽象類只能被單一繼承
(4) 抽象類更多的是定義在一系列緊密相關的類間,而介面大多數是關係疏鬆但都實現某一功能的類中
(5) 抽象類是從一系列相關物件中抽象出來的概念, 因此反映的是事物的內部共性;介面是為了滿足外部呼叫而定義的一個功能約定, 因此反映的是事物的外部特性
(6) 介面基本上不具備繼承的任何具體特點,它僅僅承諾了能夠呼叫的方法
(7) 介面可以用於支援回撥,而繼承並不具備這個特點
(8) 抽象類實現的具體方法預設為虛的,但實現介面的類中的介面方法卻預設為非虛的,當然您也可以宣告為虛的
(9) 如果抽象類實現介面,則可以把介面中方法對映到抽象類中作為抽象方法而不必實現,而在抽象類的子類中實現介面中方法
二、抽象類和介面的用法
講解了區別我們就一起看下用法,抽象類和介面我們定義下如下的類
public abstract class People{ abstract void eat(); abstract void sleep(); }
public interface People{ void eat(); void sleep(); }
我們定義了一個 abstract 和一個 interfaced,一個是介面一個是抽象類他們都需要派生的子類對父類進行方法邏輯的具體實現。這兩個是一樣的內容派生的子類對他們也是進行一樣的邏輯處理(這裡主要講解使用區別邏輯實現暫時不管),
現在我要加一個動作飛翔。
public abstract class People{ abstract void eat(); abstract void sleep(); abstract void fly(); }
public interface People{ void eat(); void sleep(); void fly(); }
這示例,明眼人一看都知道LOW的不要不要的。什麼玩意壓根就不是一類的東西。有這個感覺就對了說明還是有點變成思想的。
沒錯這個是很不合理的,這種方法違反了物件導向設計中的一個核心原則 ISP (Interface Segregation Principle),在People的定義中把People概念本身固有的行為方法和另外一個概念”飛”的行為方 法混在了一起。這樣引起的一個問題是那些僅僅依賴於Door這個概念的模組會因為”飛”這個概念的改變(比如:修改fly方法的引數)而改變,反 之依然那我們怎麼進行解決呢?
public abstract class People{ abstract void eat(); abstract void sleep(); }
public interface People{ void fly(); }
這種實現方式基本上能夠明確的反映出我們對於問題領域的理解,正確的揭示我們的設計意圖。其 實abstract class表示的是”is-a”關係,interface表示的是”like-a”關係。