黑马程序员技术交流社区

标题: 面向对象的多态怎么理解呢 [打印本页]

作者: mr_jack    时间: 2014-3-26 09:37
标题: 面向对象的多态怎么理解呢
本帖最后由 mr_jack 于 2014-3-26 16:17 编辑

面向对象的多态怎么理解呢,请举个简单的例子
作者: awkflf11    时间: 2014-3-26 10:08
————————————————
3 多态:对象的多态性。
   动物 y = new 猫(); //猫这个实体具备着两种形态,一个是猫,另一个是动物。这就是多态性。
多态在程序中的体现:父类或者接口的引用指向了自己的子类对象。

多态的前提:
        1,必须有继承或者实现的关系。
        2,通常都有覆盖的操作。
多态的好处:
        提高了程序的扩展性,在思想上也有不同,以前是面对一个对象调用,对象多了调用麻烦。
        相当于指挥一批对象做事情,将复杂事情简单化。

多态的弊端:
        多态的出现,虽然可以让前期的程序使用后期的内容。
        不可以使用子类的特有内容。

        //--------多态的好处---------
                Cat c = new Cat();
//                c.eat();
                Dog d1 = new Dog();
                Dog d2 = new Dog();
                Dog d3 = new Dog();
//                d.eat();
                method(c);
                method(d1);
        }
        public static void method(Animal a)//Animal a = new Cat(); 或者 Animal a = new Dog();
        {
                a.eat();
        }
        /*
        public static void method(Dog d)
        {
                d.eat();
        }
        public static void method(Cat d)
        {
                d.eat();
        }
        */

多态性:

Animal a = new Cat();//向上转型(类型提升)。子类对象提升为了父类型。
                /*
                提升的好处:就是提高了扩展性。隐藏了子类型。
                提升的局限性:只能使用父类中的方法。 如果子类有覆盖的话,运行的是子类的内容。


                //如何使用抓老鼠行为。
                Cat c = (Cat)a;//向下转型(强制类型转换)。好处:可以使用具体子类型的特有方法。
                c.catchMouse();

//                向下转型需要注意:父类型向下转成子类型,因为子类型不唯一,所以,需要进行判断。

                Animal an = new Cat();
//                如何判断对象类型呢? 用到一个关键字完成,instanceof   对象  instanceof  类or接口
//                记住:一旦向下转型,必须先instanceof判断
                if(an instanceof Dog)
                {
                        Dog d = (Dog)an;// ClassCastException -类型转换异常
                        d.lookHome();
                }else if(an instanceof Cat)
                {
                        Cat c = (Cat)an;
                        c.catchMouse();
                }
        //记住:对于子父类转型动作,自始自终都是子类对象在做着类型的转换而已。

——————————
4. 多态性的成员:

,成员变量:
        多态调用时,对于成员变量,无论是编译还是运行,结果只参考引用型变量(f.num)所属的类中的成员变量。
        参考等号左边。
       成员变量:
只看引用变量所属的类。
,成员函数:
        多态调用时,对于成员函数,
        编译时,参考引用型变量所属的类中是否有被调用的方法。有,编译通过,没有编译失败。
        运行时,参考的是对象所属的类中(new Zi())是否有调用的方法。
        简单说:
        编译看等号左边,运行看等号右边(子类方法覆盖父类方法)。

成员函数:

编译时:要查看引用变量所属的类中是否有所调用的成员。
在运行时:要查看对象所属的类中是否有所调用的成员。

,静态函数。
        简单说:
        编译和运行都看等号左边。



作者: zhl406893081    时间: 2014-3-26 10:34
按照封装的思想,固定不变的写在某个类里.
而这个类(接口),有多种不同实现方式,它可以是一个接口,可以是一个抽象类,可以有虚函数,可以继承父类的虚函数或是继承抽象函数,这就是这个类(接口)的多种不同的实现方式即为多态.
作者: 百川    时间: 2014-3-26 10:43
面向对象的多态就是指同一类型的对象可以表现出不同的行为。你可以理解为不同的人面对相同的事情有不同的反应。多态的实现方式有重写,抽象方法,接口。其实有相通的地方,最容易理解的是重写。
重写,即在父类方法前加virtual在子类的签名相同的同名方法前加override。
下面是示例:
  1. class Program
  2.     {
  3.         static void Main(string[] args)
  4.         {
  5.             Person[] person = new Person[3];
  6.              person[0] = new Person();
  7.              person[1] = new Person1();
  8.              person[2] = new Person2();
  9.             foreach(Person p in person)
  10.             {
  11.                 p.SayHello();
  12.             }
  13.         }
  14.     }
  15.     class Person
  16.     {
  17.         public virtual void SayHello()
  18.         {
  19.             Console.WriteLine("你好啊。");
  20.         }
  21.     }
  22.     class Person1:Person
  23.     {
  24.         public override void SayHello()
  25.         {
  26.             Console.WriteLine("你好啊。我是一号人物");
  27.         }
  28.     }
  29.     class Person2:Person
  30.     {
  31.         public override void SayHello()
  32.         {
  33.             Console.WriteLine("你好啊。我是二号人物");
  34.         }
  35.     }
复制代码







作者: threederek    时间: 2014-3-26 11:26
对于多态比较专业的解释,从这篇文章里面可以找到《重新认识抽象类和接口的区别》中一些问题的答案。可以好好看看

一、什么是多态

  面向对象程序设计中的另外一个重要概念是多态性。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。可以把一组对象放到一个数组中,然后调用它们的方法,在这种场合下,多态性作用就体现出来了,这些对象不必是相同类型的对象。当然,如果它们都继承自某个类,你可以把这些派生类,都放到一个数组中。如果这些对象都有同名方法,就可以调用每个对象的同名方法。

  同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。多态性通过派生类重载基类中的虚函数型方法来实现。

  在面向对象的系统中,多态性是一个非常重要的概念,它允许客户对一个对象进行操作,由对象来完成一系列的动作,具体实现哪个动作、如何实现由系统负责解释。

  “多态性”一词最早用于生物学,指同一种族的生物体具有相同的特性。在C#中,多态性的定义是:同一操作作用于不同的类的实例,不同的类将进行不同的解释,最后产生不同的执行结果。C#支持两种类型的多态性:

● 编译时的多态性

  编译时的多态性是通过重载来实现的。对于非虚的成员来说,系统在编译时,根据传递的参数、返回的类型等信息决定实现何种操作。

● 运行时的多态性

  运行时的多态性就是指直到系统运行时,才根据实际情况决定实现何种操作。C#中,运行时的多态性通过虚成员实现。

  编译时的多态性为我们提供了运行速度快的特点,而运行时的多态性则带来了高度灵活和抽象的特点。

二、实现多态

  多态性是类为方法(这些方法以相同的名称调用)提供不同实现方式的能力。多态性允许对类的某个方法进行调用而无需考虑该方法所提供的特定实现。例如,可能有名为 Road 的类,它调用另一个类的 Drive 方法。这另一个类 Car 可能是 SportsCar 或 SmallCar,但二者都提供 Drive 方法。虽然 Drive 方法的实现因类的不同而异,但 Road 类仍可以调用它,并且它提供的结果可由 Road 类使用和解释。

  可以用不同的方式实现组件中的多态性:

● 接口多态性。

● 继承多态性。

● 通过抽象类实现的多态性。

  接口多态性

  多个类可实现相同的“接口”,而单个类可以实现一个或多个接口。接口本质上是类需要如何响应的定义。接口描述类需要实现的方法、属性和事件,以及每个成员需要接收和返回的参数类型,但将这些成员的特定实现留给实现类去完成。

  组件编程中的一项强大技术是能够在一个对象上实现多个接口。每个接口由一小部分紧密联系的方法、属性和事件组成。通过实现接口,组件可以为要求该接口的任何其他组件提供功能,而无需考虑其中所包含的特定功能。这使后续组件的版本得以包含不同的功能而不会干扰核心功能。其他开发人员最常使用的组件功能自然是组件类本身的成员。然而,包含大量成员的组件使用起来可能比较困难。可以考虑将组件的某些功能分解出来,作为私下实现的单独接口。

  根据接口来定义功能的另一个好处是,可以通过定义和实现附加接口增量地将功能添加到组件中。优点包括:

  1.简化了设计过程,因为组件开始时可以很小,具有最小功能;之后,组件继续提供最小功能,同时不断插入其他的功能,并通过实际使用那些功能来确定合适的功能。

  2.简化了兼容性的维护,因为组件的新版本可以在添加新接口的同时继续提供现有接口。客户端应用程序的后续版本可以利用这些接口的优点。

  通过继承实现的多态性

  多个类可以从单个基类“继承”。通过继承,类在基类所在的同一实现中接收基类的所有方法、属性和事件。这样,便可根据需要来实现附加成员,而且可以重写基成员以提供不同的实现。请注意,继承类也可以实现接口,这两种技术不是互斥的。

  C# 通过继承提供多态性。对于小规模开发任务而言,这是一个功能强大的机制,但对于大规模系统,通常证明会存在问题。过分强调继承驱动的多态性一般会导致资源大规模地从编码转移到设计,这对于缩短总的开发时间没有任何帮助。

  何时使用继承驱动的多态性呢?使用继承首先是为了向现有基类添加功能。若从经过完全调试的基类框架开始,则程序员的工作效率将大大提高,方法可以增量地添加到基类而不中断版本。当应用程序设计包含多个相关类,而对于某些通用函数,这些相关类必须共享同样的实现时,您也可能希望使用继承。重叠功能可以在基类中实现,应用程序中使用的类可以从该基类中派生。抽象类合并继承和实现的功能,这在需要二者之一的元素时可能很有用。

  通过抽象类实现的多态性

  抽象类同时提供继承和接口的元素。抽象类本身不能实例化,它必须被继承。该类的部分或全部成员可能未实现,该实现由继承类提供。已实现的成员仍可被重写,并且继承类仍可以实现附加接口或其他功能。

  抽象类提供继承和接口实现的功能。抽象类不能示例化,必须在继承类中实现。它可以包含已实现的方法和属性,但也可以包含未实现的过程,这些未实现过程必须在继承类中实现。这使您得以在类的某些方法中提供不变级功能,同时为其他过程保持灵活性选项打开。抽象类的另一个好处是:当要求组件的新版本时,可根据需要将附加方法添加到基类,但接口必须保持不变。

  何时使用抽象类呢?当需要一组相关组件来包含一组具有相同功能的方法,但同时要求在其他方法实现中具有灵活性时,可以使用抽象类。当预料可能出现版本问题时,抽象类也具有价值,因为基类比较灵活并易于被修改。
示例:实现多态性的程序



using System ;

public class DrawingBase

{

public virtual void Draw( )

{

Console.WriteLine("Im just a generic drawing object.") ;

}

}

public class Line : DrawingBase

{

public override void Draw( )

{ Console.WriteLine("Im a Line.") ; }

}

public class Circle : DrawingBase

{

public override void Draw( )

{ Console.WriteLine("Im a Circle.") ; }

}

public class Square : DrawingBase

{

public override void Draw( )

{ Console.WriteLine("Im a Square.") ; }

}

public class DrawDemo

{

public static int Main(string[] args)

{

DrawingBase [] dObj = new DrawingBase [4];

dObj[0] = new Line( ) ;

dObj[1] = new Circle( ) ;

dObj[2] = new Square( ) ;

dObj[3] = new DrawingBase( ) ;

foreach (DrawingBase drawObj in dObj)

drawObj.Draw( ) ;

return 0;

}

}


  说明:上面程序演示了多态性的实现。在DrawDemo类中的Main( )方法中,创建了一个数组,数组元素是DrawingBase类的对象。该数组名为dObj,是由四个DrawingBase类型的对象组成。接下来,初始化dObj数组,由于Line,Circle和Square类都是DrawingBase类的派生类,所以这些类可以作为dObj数组元素的类型。如果C#没有这种功能,你得为每个类创建一个数组。继承的性质可以让派生对象当作基类成员一样用,这样就节省了编程工作量。 一旦数组初始化之后,接着是执行foreach循环,寻找数组中的每个元素。在每次循环中,dObj 数组的每个元素(对象)调用其Draw( )方法。多态性体现在:在运行时,各自调用每个对象的Draw( )方法。尽管dObj 数组中的引用对象类型是DrawingBase,这并不影响派生类重载DrawingBase类的虚方法Draw( )。 在dObj 数组中,通过指向DrawingBase基类的指针来调用派生类中的重载的Draw( )方法。

  输出结果是:

Im a Line.

Im a Circle.

Im a Square.

Im just a generic drawing object.

  在DrawDemo 程序中,调用了每个派生类的重载的Draw( )方法。 最后一行中,执行的是DrawingBase类的虚方法Draw( )。这是因为运行到最后,数组的第四个元素是DrawingBase类的对象。  //这里是应该是重写override

三、虚方法

  当类中的方法声明前加上了virtual 修饰符,我们称之为虚方法,反之为非虚。使用了virtual 修饰符后,不允许再有static, abstract, 或override 修饰符。

示例1:带有虚方法的类



using System ;

public class Drawin




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2