近来看了一下多态性把我的一些感受以例子的形式记录一下 一形象理解 两条理解的原则 ()一个派生类对象可以被声明成一个基类或者是一个基类指针可以指向一个派生类对象 //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的定义 那就是说这个方法是属于接口的但是接口不能含有具体的实现代码这里是不是存在一定的矛盾呢? |