• 首页
  • 小学语文
  • 中学语文
  • 中学英语
  • 免费论文
  • 教学随笔
  • 学生作文
  • 综合考试
  • 试题教案
  • 育儿话题
  • 教学资源
  • 编程技术
  • 博客
  • 实例看继承与多态

    日期:2003-03-04  地址:  作者:
    实例看继承与多态

    实例看多态

     

    近来看了一下多态性,把我的一些感受以例子的形式记录一下。

     

    一.形象理解
    两条理解的原则:
    1)一个派生类对象可以被声明成一个基类,或者是一个基类指针可以指向一个派生类对象:
    //c++ code

    BaseClass *p;

    DerivedClass obj;

    p = &obj;

     

    //C# code

    BaseClass obj = new DerivedClass();


    2)把一个对象看做是一个独立的个体,调用对象的public成员函数实际上是给这个对象发送一个消息,采取什么样的动作完全由对象自己决定。

     

    Shape是基类,CircleLine是从Shape继承出来的,Shapedraw()方法,CircleLine分别自己定义了自己的draw()方法,在下面的代码里:

    // Java Code

    static void func(Shape s)

    {

    s.Draw();

    }

    如果发生了这样的调用:

    Line l = new Line();

    Circle c = new Circle();

    func(l);

    func( c);

    一个Circle和一个Line被当做Shape传到函数里去了,然后调用Draw(),会发生什么情况?因为对象是独立的个体,在func()里,这两个对象被分别传递了Draw()消息,叫它们绘制自己吧,于是他们分别调用了自己类里定义的Draw()动作。

     

    通过这两条原则我们可以理解上面的多态。正是由于多态,使得我们不必要这样去做:

    IF 你是一个Circle THEN 调用CircleDraw()

    ELSE IF 你是一个Line THEN 调用LineDraw()

    ELSE …

    我们只要给这个被声明成为Shape的对象发送Draw消息,怎么样去Draw就由对象自己去决定了。

     

    二.一切皆因虚函数

     

    先看看实现多态的基本条件:

    (1)      基类含有虚函数

    (2)      继承类把这个虚函数重新实现了

    (3)      继承类也可能没有重新实现基类的所有虚函数,因此对于这些没有被重新实现的虚函数不能发生多态。

     

    再看一下几种语言里一些特别的规定:

    1. C++
    1)虚函数用virtual关键字声明。
    2virtual void Func(para_list) = 0;这样的虚函数叫做纯虚函数,表示这个函数没有具体实现。包含纯虚函数的类叫做抽象类,如果他的继承类没有对这个纯虚函数具体用代码实现,则这个继承类也是抽象类。抽象类不能被实例话(就是说不能创建出对象)。
    3)继承类重新实现基类的虚函数时,不需要做任何特别的声明。
    4)如果不用virtual关键字修饰,并且在派生类里重新实现了这个方法,这仅仅是一个简单的覆盖,不会发生多态,我们暂称它非多态吧。

    2. Java
    1Java没有virtual关键字,Java把一切类的方法都认为是虚函数。
    2)继承类重新实现基类的虚函数时,不需要做任何特别的声明。因此在Java里只要重新实现了基类的方法,并且把继承类对象声明为基类,多态就要发生。因此Java对多态的条件相对是比较低的。

    //Java Code
    class BaseClass
    {
      public void hello(){};
    }

    class DerivedClass extends BaseClass
    {
      public void hello()
      {
        System.out.println(“Helloworld!”);
      }
     
      public static void main(Stringargs[])
      {
        BaseClass obj = newDerivedClass();
        obj.hello();
      }
    }

    输入是Helloworld!。这样就实现了多态。

    3)虚函数用abstract声明,含有虚函数的类是抽象类,也要用abstract关键字修饰。

    //Java Code
    public abstract AbstractClass
    {
      public abstract void hello();
      //…
    }

    3. C#
    C#对于多态的编写是最为严格和严谨的。
    1)虚函数用virtual声明。
    2)纯虚函数用abstract声明,含纯虚函数的类是抽象类,必须用abstract关键字修饰。
    3)如果仅仅是覆盖基类的非虚方法,则需要用new关键字声明:
    //C# Code
    public class BaseClass
    {
      public void hello()
      {
         System.Console.WriteLine(“Hello,this come from BaseClass”);
      }
    }

    public class DerivedClass : BaseClass
    {
      public new void hello()
      {
       System.Console.WriteLine(“Hello,this is come from DerivedClass”);
      }

      public static void Main()
      {
        BaseClass obj = newDerivedClass();
        obj.hello();
      }
    }

    输出为Hello,thiscome from BaseClass,也就是说这并没有实现多态(非多态)。

    4)通过virtual – overrideabstract – override组合实现多态。
    当派生类重新实现基类的虚函数(或纯虚函数)时,必须用override关键字进行修饰。

    //C# Code
    public abstract class AbsBaseClass
    {
      public abstract void hello();
    }

    public class DerivedClass : AbsBaseClass
    {
      public void hello()
      {
       System.Console.WriteLine(“Hello world!”);
      }

      public static voidSayHello(AbsBaseClass obj)
      {
        obj.hello();
      }

      public static void Main()
      {
        DerivedClass _obj =new DerivedClass();
       DerivedClass.SayHello(_obj);
      }
    }

    输出为Helloworld!

     

    三.多态的反溯
    继承类对象在发生多态时,并是不完全抛开基类不管的,它会去查看基类的虚函数列表,在这个列表的范围内才会发生多态。
    让我们来看一个比较复杂的例子:

    // Java Code
    class A

    {

           protectedvoid hello(Object o)

           {

                  System.out.println("A- Object");

           }

    }

     

    class B extends A

    {

           protectedvoid hello(String s)

           {

                  System.out.println("B- String");

           }

     

           protectedvoid hello(Object o)

           {

                  System.out.println("B- Object");

           }

    };

     

    class C

    {

           publicstatic void main(String args[])

           {                  

                  Objectobj = new Object();          

                  Stringstr = "ABC";

                  Aa = new B();      

     

                  a.hello(obj);          

                  a.hello(str);

           }

    };

     

    输出结果为:

    B – Object

    B – Object

     

    正如上面所说的,由于基类里没有参数类型为String的虚函数,因此Bhello(String)方法不参与多态。调用a.hello(str)时,由于StringObject的继承类,因此这个str被作为一个Object传入了Bhello(Object),这一点正如我们的原则一所述。

     

    四.接口——仅仅是更抽象的抽象类

           接口是类的协定,但由于接口又参与多态性,从这一点说,我们认为它是更为抽象的抽象类,如下:

     

    // Java Code
    interface IBase

    {
           voidhello();
    }

    class DerivedClass implements IBase
    {
           publicvoid hello()
           {
                  System.out.println(“Helloworld!”);
           }

           publicstatic void main(String args[])
           {
                  IBaseobj = new DerivedClass();
                  obj.hello();
           }
    }

     

    JavaC#中,类只能从一个基类派生出来,但是可以实现多个接口。

    这里有一个小小的问题:如果IBase1IBase2里都声明了有hello()方法,DerivedClass实现了这两个接口,当然需要具体把hello()实现出来。

    interface IBase1
    {
           voidhello();
    }

    interface IBase2
    {
           voidhello();
    }

    public class DerivedClass1 : IBase1,IBase2
    {
           publicvoid hello()
           {
                  System.Console.WriteLine(“Helloworld!”);
           }
    }

    public class DerivedClass2 : IBase1,IBase2
    {
           voidIBase1.hello()

           {
                  System.Console.WriteLine(“Thiscome from IBase1”);
           }

           voidIBase2.hello()
           {
                  System.Console.WriteLine(“Thiscome from IBase2”);
           }

           publicstatic void Main()
           {
                  IBase1obj_1 = new DerivedClass1();
                  IBase2obj_2 = new DerivedClass1();
                  IBase1obj_3 = new DerivedClass2();
                  IBase2obj_4 = new DerivedClass2();

                  obj_1.hello();
                  obj_2.hello();
                  obj_3.hello();
                  obj_4.hello();
           }
    }

     

    输出为:

    Hello world!

    Hello world!;

    This come from IBase1

    This come from IBase2

     

    有两点注意:(1DerivedClass2的实现方法叫显式实现,这种方法C#才支持,在Java里不能实现。(2)进一步测试表明:hello()方法并不属于DerivedClass2

    加入这样的代码

    DerivedClass2 t = new DerivedClass2();

    t.hello();

    编译错误:test.cs(44,3):error CS0117: DerivedClass2”并不包含对“hello”的定义

    那就是说这个方法是属于接口的,但是接口不能含有具体的实现代码,这里是不是存在一定的矛盾呢?

     

    欢迎与我交流:tanrui@sjtu.edu.cn

    对 实例看继承与多态 文章的评论    [查看网友评论]

    验证码:
    匿名发表: