前言
很多新人對這個問題已經看到的厭倦或者是惡心了,有可能是因為比較難理解或者是未理解的情況下對它們的認知不夠所以產生的想法是差不多,不用抽獎類和接口我一樣能實現我想要的功能。
這句話確實不假,但是實現的功能是否符合面向對象思想就不得而知了,也有很多的朋友知道它們的意思但是許久不用也忘記的差不多了,那么我們今天就在來鞏固下 抽象類和接口的一些區別。
為什么頻頻出現這個問題,無論是在面試過程中還是面試題上很多公司都在問這個問題,考驗的無非幾點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"關系。