熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

從實例中看多態

2013-11-13 10:06:36  來源: .NET編程 
    近來看了一下多態性把我的一些感受以例子的形式記錄一下
    一形象理解
    兩條理解的原則
    ()一個派生類對象可以被聲明成一個基類或者是一個基類指針可以指向一個派生類對象
    //c++ code
    BaseClass *p;
    DerivedClass obj;
    p = &obj;
    //C# code
    BaseClass obj = new DerivedClass()
    ()把一個對象看做是一個獨立的個體調用對象的public成員函數實際上是給這個對象發送一個消息采取什麼樣的動作完全由對象自己決定
    Shape是基類Circle和Line是從Shape繼承出來的Shape有draw()方法Circle與Line分別自己定義了自己的draw()方法在下面的代碼裡
    // Java Code
    static void func(Shape s)
    {
    sDraw()
    }
    如果發生了這樣的調用
    Line l = new Line()
    Circle c = new Circle()
    func(l)
    func( c)
    一個Circle和一個Line被當做Shape傳到函數裡去了然後調用Draw()會發生什麼情況?因為對象是獨立的個體在func()裡這兩個對象被分別傳遞了Draw()消息叫它們繪制自己吧於是他們分別調用了自己類裡定義的Draw()動作
    通過這兩條原則我們可以理解上面的多態正是由於多態使得我們不必要這樣去做
    IF 你是一個Circle THEN 調用Circle的Draw()
    ELSE IF 你是一個Line THEN 調用Line的Draw()
    ELSE …
    我們只要給這個被聲明成為Shape的對象發送Draw消息怎麼樣去Draw就由對象自己去決定了
    二一切皆因虛函數
    先看看實現多態的基本條件
    () 基類含有虛函數
    () 繼承類把這個虛函數重新實現了
    () 繼承類也可能沒有重新實現基類的所有虛函數因此對於這些沒有被重新實現的虛函數不能發生多態
    再看一下幾種語言裡一些特別的規定
    C++
    ()虛函數用virtual關鍵字聲明
    ()virtual void Func(para_list) = ;這樣的虛函數叫做純虛函數表示這個函數沒有具體實現包含純虛函數的類叫做抽象類如果他的繼承類沒有對這個純虛函數具體用代碼實現則這個繼承類也是抽象類抽象類不能被實例話(就是說不能創建出對象)
    ()繼承類重新實現基類的虛函數時不需要做任何特別的聲明
    ()如果不用virtual關鍵字修飾並且在派生類裡重新實現了這個方法這僅僅是一個簡單的覆蓋不會發生多態我們暫稱它非多態吧


    Java
    ()Java沒有virtual關鍵字Java把一切類的方法都認為是虛函數
    ()繼承類重新實現基類的虛函數時不需要做任何特別的聲明因此在Java裡只要重新實現了基類的方法並且把繼承類對象聲明為基類多態就要發生因此Java對多態的條件相對是比較低的
    //Java Code
    class BaseClass
    {
    public void hello(){};
    }
    class DerivedClass extends BaseClass
    {
    public void hello()
    {
    Systemoutprintln(Hello world!
    }
    public static void main(String args[])
    {
    BaseClass obj = new DerivedClass()
    objhello()
    }
    }
    輸入是Hello world!這樣就實現了多態
    ()虛函數用abstract聲明含有虛函數的類是抽象類也要用abstract關鍵字修飾
    //Java Code
    public abstract AbstractClass
    {
    public abstract void hello()
    //…
    }
    C#
    C#對於多態的編寫是最為嚴格和嚴謹的
    ()虛函數用virtual聲明
    ()純虛函數用abstract聲明含純虛函數的類是抽象類必須用abstract關鍵字修飾
    ()如果僅僅是覆蓋基類的非虛方法則需要用new關鍵字聲明
    //C# Code
    public class BaseClass
    {
    public void hello()
    {
    SystemConsoleWriteLine(Hellothis come from BaseClass
    }
    }
    public class DerivedClass : BaseClass
    {
    public new void hello()
    {
    SystemConsoleWriteLine(Hellothis is come from DerivedClass
    }
    public static void Main()
    {
    BaseClass obj = new DerivedClass()
    objhello()
    }
    }
    輸出為Hellothis come from BaseClass也就是說這並沒有實現多態(非多態)
    ()通過virtual – overrideabstract – override組合實現多態
    當派生類重新實現基類的虛函數(或純虛函數)時必須用override關鍵字進行修飾
    //C# Code
    public abstract class AbsBaseClass
    {
    public abstract void hello()
    }
    public class DerivedClass : AbsBaseClass
    {
    public void hello()
    {
    SystemConsoleWriteLine(Hello world!
    }
    public static void SayHello(AbsBaseClass obj)
    {
    objhello()
    }
    public static void Main()
    {
    DerivedClass _obj = new DerivedClass()
    DerivedClassSayHello(_obj)
    }
    }
    輸出為Hello world!


    三多態的反溯
    繼承類對象在發生多態時並是不完全拋開基類不管的它會去查看基類的虛函數列表在這個列表的范圍內才會發生多態
    讓我們來看一個比較復雜的例子
    // Java Code
    class A
    {
    protected void hello(Object o)
    {
    Systemoutprintln(A Object
    }
    }
    class B extends A
    {
    protected void hello(String s)
    {
    Systemoutprintln(B String
    }
    protected void hello(Object o)
    {
    Systemoutprintln(B Object
    }
    };
    class C
    {
    public static void main(String args[])
    {
    Object obj = new Object()
    String str = ABC;
    A a = new B()
    ahello(obj)
    ahello(str)
    }
    };
    輸出結果為
    B – Object
    B – Object
    正如上面所說的由於基類裡沒有參數類型為String的虛函數因此B的hello(String)方法不參與多態調用ahello(str)時由於String是Object的繼承類因此這個str被作為一個Object傳入了B的hello(Object)這一點正如我們的原則一所述
    四接口——僅僅是更抽象的抽象類
    接口是類的協定但由於接口又參與多態性從這一點說我們認為它是更為抽象的抽象類如下
    // Java Code
    interface IBase
    {
    void hello()
    }
    class DerivedClass implements IBase
    {
    public void hello()
    {
    Systemoutprintln(Hello world!
    }
    public static void main(String args[])
    {
    IBase obj = new DerivedClass()
    objhello()
    }
    }
    在Java與C#中類只能從一個基類派生出來但是可以實現多個接口
    這裡有一個小小的問題如果IBase與IBase裡都聲明了有hello()方法DerivedClass實現了這兩個接口當然需要具體把hello()實現出來
    interface IBase
    {
    void hello()
    }
    interface IBase
    {
    void hello()
    }
    public class DerivedClass : IBaseIBase
    {
    public void hello()
    {
    SystemConsoleWriteLine(Hello world!
    }
    }
    public class DerivedClass : IBaseIBase
    {
    void IBasehello()
    {
    SystemConsoleWriteLine(This come from IBase
    }
    void IBasehello()
    {
    SystemConsoleWriteLine(This come from IBase
    }
    public static void Main()
    {
    IBase obj_ = new DerivedClass()
    IBase obj_ = new DerivedClass()
    IBase obj_ = new DerivedClass()
    IBase obj_ = new DerivedClass()
    obj_hello()
    obj_hello()
    obj_hello()
    obj_hello()
    }
    }
    輸出為
    Hello world!
    Hello world!;
    This come from IBase
    This come from IBase
    有兩點注意)DerivedClass的實現方法叫顯式實現這種方法C#才支持在Java裡不能實現)進一步測試表明hello()方法並不屬於DerivedClass
    加入這樣的代碼
    DerivedClass t = new DerivedClass()
    thello()
    編譯錯誤testcs( error CS: DerivedClass並不包含對hello的定義
    那就是說這個方法是屬於接口的但是接口不能含有具體的實現代碼這裡是不是存在一定的矛盾呢?


From:http://tw.wingwit.com/Article/program/net/201311/12588.html
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.