实例看多态

王朝c#·作者佚名  2006-12-17
窄屏简体版  字體:   |    |    |  超大  

实例看继承与多态 tanrui tanrui 2 130 2004-03-07T14:24:00Z 2004-03-07T14:24:00Z 1 737 4203 SJTU 35 9 4931 11.5606 7.8 磅 0 2 false false false MicrosoftInternetExplorer4 st1\:*{behavior:url(#ieooui) } /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.0pt; font-family:"Times New Roman"; mso-ansi-language:#0400; mso-fareast-language:#0400; mso-bidi-language:#0400;}实例看多态

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

一.形象理解

两条理解的原则:

(1)一个派生类对象可以被声明成一个基类,或者是一个基类指针可以指向一个派生类对象:

//c++ code

BaseClass *p;

DerivedClass obj;

p = &obj;

//C# code

BaseClass obj = new DerivedClass();

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

Shape是基类,Circle和Line是从Shape继承出来的,Shape有draw()方法,Circle与Line分别自己定义了自己的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 调用Circle的Draw()

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

ELSE …

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

二.一切皆因虚函数

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

(1) 基类含有虚函数

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

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

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

1. C++:

(1)虚函数用virtual关键字声明。

(2)virtual void Func(para_list) = 0;这样的虚函数叫做纯虚函数,表示这个函数没有具体实现。包含纯虚函数的类叫做抽象类,如果他的继承类没有对这个纯虚函数具体用代码实现,则这个继承类也是抽象类。抽象类不能被实例话(就是说不能创建出对象)。

(3)继承类重新实现基类的虚函数时,不需要做任何特别的声明。

(4)如果不用virtual关键字修饰,并且在派生类里重新实现了这个方法,这仅仅是一个简单的覆盖,不会发生多态,我们暂称它非多态吧。

2. Java:

(1)Java没有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 – override、abstract – 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的虚函数,因此B的hello(String)方法不参与多态。调用a.hello(str)时,由于String是Object的继承类,因此这个str被作为一个Object传入了B的hello(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();

}

}

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

这里有一个小小的问题:如果IBase1与IBase2里都声明了有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

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

加入这样的代码

DerivedClass2 t = new DerivedClass2();

t.hello();

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

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

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

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航