URLhttps://learnscript.net/zh-hant/programming/object-oriented/class-polymorphism/
    複製連結移至說明  範例

    類別的多型,方法覆寫,方法多載,方法遮蔽介紹

    閱讀 9:34·字數 2873·發佈 
    Youtube 頻道
    訂閱 375

    先決條件

    閱讀本節的先決條件是對物件導向程式設計,類別,執行個體等概念有所掌握,你可以檢視物件導向程式設計,類別,執行個體介紹一節來了解他們。

    類別的多型

    類別的多型是指衍生類別可以改變調整繼承成員的實作,以符合自身的需求,這主要針對執行個體方法,或本質上是方法的執行個體成員。多型使得繼承與被繼承的物件,在同一功能上的表現可以不同。

    變數型別與多型

    在支援物件導向和宣告變數型別的語言中,變數的型別與值的資料型別可能不同,通過變數呼叫的方法,將指向物件對該方法的真實實作,當物件已經對方法做出調整或改變時。

    在下面的 C# 程式碼中,雖然變數kittyanimal的型別都是Animal,且Animal類別的Bark方法固定,但實際呼叫Bark方法卻顯示了不同的資訊,因為變數kitty對應的真實物件的型別是Kitty,對於Bark方法,Kitty類別擁有自己的實作。

    *.cs
    // 建立 Animal 和 Kitty 的執行個體
    Animal animal = new Animal();
    Animal kitty = new Kitty();
    
    // 分別呼叫 Bark 方法,將顯示不同資訊 Console.WriteLine("animal.Bark"); animal.Bark(); Console.WriteLine("kitty.Bark"); kitty.Bark();
    animal.Bark
    試著發出聲音?
    kitty.Bark
    試著發出聲音?
    喵喵喵!

    方法覆寫

    方法覆寫(Method Overriding)是實作多型的最簡單方式,一般的做法是在類別中再次定義需要覆寫的執行個體方法,此過程可能需要特定的關鍵字,比如,C# 中的override

    什麽是虛擬方法?

    虛擬方法是指允許被衍生類別覆寫的方法,通常對應關鍵字virtual。在一些語言中,虛擬方法是不存在的,因為預設所有方法均可被覆寫。

    由於虛擬方法允許被覆寫,所以他不應該具有任何封鎖該目標實作的特征,比如,私用的。

    在衍生類別中呼叫基底類別的方法

    在方法覆寫的過程中,你可能需要使用基底類別的方法,如果覆寫的目的在於補充功能。大部分語言會提供相關的關鍵字來完成此目標,比如,C# 中的base關鍵字。

    下面展示了之前範例中使用的類別AnimalKitty,可以看到Animal類別擁有一個虛擬方法BarkKitty類別對該方法進行了覆寫,並呼叫了基底類別的Bark方法。

    class_polymorphism.cs
    // 基底類別 Animal
    class Animal
    {
    	// 虛擬方法 Bark,嘗試發出聲音
    	public virtual void Bark()
    	{
    		Console.WriteLine("試著發出聲音?");
    	}
    }
    
    // 衍生類別 Kitty class Kitty : Animal { // Kitty 覆寫了繼承自 Animal 的方法 Bark public override void Bark() { // 呼叫基底類別的 Bark 方法 base.Bark();
    Console.WriteLine("喵喵喵!"); } }

    什麽是抽象方法和抽象類別?

    抽象方法是類別中已經宣告,但尚未具體實作的方法,他通常需要特定的關鍵字來標註,比如,C# 中的abstract,並需要衍生類別來完成實作,一個方法被抽象的原因是其必要性和缺少通用性的邏輯。

    由於需要通過衍生類別完成實作,抽象方法不能具有任何封鎖這一目標實作的特征,比如,私用的。

    擁有抽象方法的類別被稱為抽象類別,因為沒有完全的具體實作,抽象類別無法被具現化。需要指出,抽象類別包括了,已繼承抽象方法但仍未對其實作的衍生類別。

    抽象方法和虛擬方法之間的區別

    抽象方法要求被具體實作,否則相關類別無法具現化,虛擬方法則表示允許被覆寫,能否具現化與是否被覆寫無關。

    下面,為Animal類別增加一個抽象方法RunKitty類別需要實作該方法後才能被具現化。

    class_polymorphism.cs
    // 基底類別 Animal
    abstract class Animal
    {
    	// …
    	// 抽象方法 Run,奔跑起來
    	public abstract void Run();
    }
    
    // 衍生類別 Kitty class Kitty : Animal { // … // Kitty 必須實作抽象方法 Run,才能被具現化 public override void Run() { Console.WriteLine("你看,我跑起來了!"); } }
    *.cs
    // 建立 Kitty 的執行個體
    Kitty kitty = new Kitty();
    
    // 呼叫 Run 方法 kitty.Run();
    你看,我跑起來了!

    方法多載

    方法多載(Method Overloading)與多型的關系不大,他允許一個類別定義多個名稱相同但簽章不同的方法,而不是將基底類別中的方法再次定義。方法多載在一些語言中是不被支援或不可行的,他們不允許一個類別擁有名稱相同的方法,方法簽章僅通過方法名稱產生,或者根本沒有簽章。

    覆寫基底類別的多載方法需要比對方法簽章

    這裏需要指出,在覆寫基底類別的多載方法時,請註意方法簽章是否相符。

    選擇方法多載還是選擇性參數?

    當方法內部的邏輯差異不大時,使用選擇性參數應該是更好,如果語言支援這一功能的話,你將不會看到一排排的方法定義,僅僅是為了多出或缺少的參數。

    當不同參數組合帶來的邏輯差異較大時,可選擇使用方法多載,以使程式碼更容易維護。

    函式

    要了解方法簽章,你可以檢視函式簽章一段,好吧,名字不代表一切。

    下面,通過多載為Kitty類別新增兩個同名的Eat方法,他們擁有的參數不同。

    class_polymorphism.cs
    // 衍生類別 Kitty
    class Kitty : Animal
    {
    	// …
    	// 使用多載定義兩個同名的 Eat 方法
    	public void Eat(string something)
    	{
    		Console.WriteLine($"好耶,今天的午餐是:{something}");
    	}
    	public void Eat()
    	{
    		Console.WriteLine("今天吃空氣?!");
    	}
    }
    *.cs
    // 建立 Kitty 的執行個體
    Kitty kitty = new Kitty();
    
    // 呼叫多載的 Eat 方法 kitty.Eat("小魚幹"); kitty.Eat();
    好耶,今天的午餐是:小魚幹
    今天吃空氣?!

    方法遮蔽

    方法遮蔽(Method Hiding/Shadowing)和方法覆寫是比較容易混淆的,他們均針對基底類別的方法進行重新定義,但不同的是,通過遮蔽定義的方法斷絕了自身與基底類別方法的繼承關系,通過覆寫定義的方法則會保持。方法遮蔽和方法覆寫的選擇,將影響多型的表現效果。

    在通過某種型別的變數呼叫方法時,需要確定實際物件是否存在對該方法的覆寫,而通過遮蔽定義的方法將打斷這一過程,先決條件是這些方法真實的阻斷了目前和之後的繼承關系,他們不能是私用的,或具有任何導致上述目標無法實作的特征。

    在下面的 C# 程式碼中,變數abcd的型別均為A,但變數b對應的真實物件的型別為B,類別B覆寫了類別AShow方法,因此b.Show()指向類別BShow方法的實作。變數c對應的真實物件的型別為C,類別C遮蔽了類別BShow方法,c.Show()將不會指向類別CShow方法的實作,因為類別CShow方法與類別BAShow方法不存在繼承關系。變數d對應的真實物件的型別為D,類別D覆寫了類別CShow方法,d.Show()將不會指向類別DCShow方法的實作,因為類別DCShow方法與類別BAShow方法不存在繼承關系。

    *.cs
    // 所有變數的型別均為 A,但真實的物件型別分別為 A,B,C,D
    A a = new A();
    A b = new B();
    A c = new C();
    A d = new D();
    
    // 分別呼叫他們的 Show 方法 a.Show(); b.Show(); c.Show(); d.Show();
    呼叫了 A 的 Show 方法
    呼叫了 B 的 Show 方法
    呼叫了 B 的 Show 方法
    呼叫了 B 的 Show 方法
    class_polymorphism.cs
    // 類別 A
    class A
    {
    	// 虛擬方法 Show
    	public virtual void Show()
    	{
    		Console.WriteLine("呼叫了 A 的 Show 方法");
    	}
    }
    
    // 類別 B class B : A { // 覆寫類別 A 的 Show 方法 public override void Show() { Console.WriteLine("呼叫了 B 的 Show 方法"); } }
    // 類別 C class C : B { // 遮蔽類別 B 的 Show 方法,阻斷了繼承關系 public new virtual void Show() { Console.WriteLine("呼叫了 C 的 Show 方法"); } }
    // 類別 D class D : C { // 覆寫類別 C 的 Show 方法 public override void Show() { Console.WriteLine("呼叫了 D 的 Show 方法"); } }

    原始碼

    class_polymorphism.cs·codebeatme/programming·GitHub