首 页 | 新 闻 | 技术中心 | 第二书店 | 《程序员》 | 《开发高手》 | 社 区 | 黄 页 | 人 才
移 动专 题SUNIBM微 软微 创精 华Donews人 邮
我的技术中心 
我的分类 我的文档
全部文章 发表文章
专栏管理 使用说明



 RSS 订阅 
最新文档列表
Windows/.NET
.NET  (rss)    
Visual C++  (rss)    
Delphi  (rss)    
Visual Basic  (rss)    
ASP  (rss)    
JavaScript  (rss)    
Java/Linux
Java  (rss)    
Perl  (rss)    
综合
其他开发语言  (rss)    
文件格式  (rss)    
企业开发
游戏开发  (rss)    
网站制作技术  (rss)    
数据库
数据库开发  (rss)    
软件工程
其他  (rss)    

积极原创作者 
nizhigang2000 (11)
coofucoo (110)
iyanglian (101)
Aoouch (11)
NinGoo (28)
amh (72)
superyan (27)
iiprogram (83)
hongbo781202 (81)
Kendiv (113)
CSDN - 文档中心 - .NET 阅读:2617   评论: 5    参与评论
标题   实例看多态     选择自 tanrui 的 Blog
关键字   多态性 继承
出处  
实例看继承与多态

实例看多态

 

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

 

一.形象理解
两条理解的原则:
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(“Hello world!”);
  }
 
  public static void main(String args[])
  {
    BaseClass obj = new DerivedClass();
    obj.hello();
  }
}

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

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 = new DerivedClass();
    obj.hello();
  }
}

输出为Hello,this come 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 void SayHello(AbsBaseClass obj)
  {
    obj.hello();
  }

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

输出为Hello world!

 

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

// Java Code
class A

{

       protected void hello(Object o)

       {

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

       }

}

 

class B extends A

{

       protected void hello(String s)

       {

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

       }

 

       protected void hello(Object o)

       {

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

       }

};

 

class C

{

       public static void main(String args[])

       {                  

              Object obj = new Object();          

              String str = "ABC";

              A a = 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

{
       void hello();
}

class DerivedClass implements IBase
{
       public void hello()
       {
              System.out.println(“Hello world!”);
       }

       public static void main(String args[])
       {
              IBase obj = new DerivedClass();
              obj.hello();
       }
}

 

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

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

interface IBase1
{
       void hello();
}

interface IBase2
{
       void hello();
}

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

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

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

       void IBase2.hello()
       {
              System.Console.WriteLine(“This come from IBase2”);
       }

       public static void Main()
       {
              IBase1 obj_1 = new DerivedClass1();
              IBase2 obj_2 = new DerivedClass1();
              IBase1 obj_3 = new DerivedClass2();
              IBase2 obj_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


相关文章
对该文的评论
tanrui ( 2004-03-24)
To Ani:

我的意思是说只有A::Hello(Object) -- B::Hello(Object)这一对才会发生多态。
Ani ( 2004-03-23)
三.多态的反溯
调用a.hello(str)时,由于String是Object的继承类,因此这个str被作为一个Object传入了B的hello(Object),这一点正如我们的原则一所述

对上面的表示怀疑
tanrui ( 2004-03-19)
VB我不懂:)
Ninputer ( 2004-03-19)
VB在这一方面有更复杂的结构:
1、指定按非虚函数的的方法访问一个虚函数
2、直接实现有重名的接口
3、直接指定非虚函数:NotInheritable

wr960204 ( 2004-03-11)
有些观点我不能同意。但也不能说我认为的就是对的