您的位置: 网站首页 > 程序开发 > Java程序设计 > 第4章 继承与多态 > 【4.4 多 态】

4.4 多 态

 

4.4     

多态性是面向对象程序设计的又一个重要的技术和手段。多态性是指同名的不同方法在程序中共存。即为同一个方法定义几个版本,运行时根据不同情况执行不同的版本。调用者只需使用同一个方法名,系统会根据不同情况,调用相应的不同方法,从而实现不同的功能。多态性即“一个名字,多个方法”。

4.4.1  多态性的概念

在面向过程的程序设计中,各函数是不能重名的,否则在使用名字调用时就会产生歧义和错误;而在面向对象的程序设计中,有时却需要利用这样的“重名”现象来提高程序的抽象度和简洁性。

4-2中的电话类的层次树,计费方式是所有电话都具有的操作,但不同的电话计费方式操作的具体实现是不同的。如果不允许这些目标和功能相同的操作使用同样的方法名字,就必须分别定义多个不同名字的方法。这样使用起来要区分多个方法的名字,很不方便,继承的优势就不明显了。在面向对象的程序设计中,为了解决这个问题,引入了多态性的概念。

多态性的实现有以下两种方式:

·    覆盖实现多态性:通过子类对父类方法的重定义来实现。使用时注意,在子类重定义父类方法时,要求与父类中方法的原型(参数个数、参数类型、参数顺序)完全相同。

·    重载实现多态性:通过定义类中的多个同名的不同方法来实现。编译时是根据参数(个数、类型、顺序)的不同来区分不同方法的。

4.4.2  覆盖实现多态性

以图4-2中各类电话为例,Telephone类有一个charge_Mode ( )方法代表计费功能,根据继承的特点,Telephone类的每个子类都将继承这个方法。但是,该方法的功能在不同种类的电话中的具体实现是不同的。因此,不同的子类可以重新定义、编写charge_Mode( )方法的内容,以实现特定计费方式。在这些子类中,虽然实现的方式不同,但却共享同一个方法名charge_Mode( )

在覆盖实现多态性的方式中,当子类重定义父类方法时,方法的名字、参数个数、参数类型、参数顺序完全相同,那么如何区别这些同名的不同方法呢?由于这些方法是存在于一个类层次结构的不同子类中的,在调用方法时只需要指明调用哪个类(或对象)的方法,就很容易把它们区分开来,其调用形式如下:

对象名.方法名

类名.方法名

例如,IP电话的计费,若建立IP_Phone类的对象my,其调用如下。

my.charge_Mode();

假如charge_Mode( )是一个类方法,则要使用类名,其调用如下。

IP_Phone.charge_Mode();

程序运行时,系统会分辨电话的类型,并调用相应的具体计费的方法,从而实现计费功能的多态性。

【例4-7使用父类对象和子类对象调用同名方法时,会得出不同的调用结果。

import java.applet.*;

import java.awt.*;

class area{

    double f(double r){

        return  3.14*r*r;

    }

    double g(double x,double y){

        return  0.5*x*y;

    }

}

class circle extends area{   

    double f(double r){

        return  3.14*2.0*r;

    }

}

public class PolyMorphism_Example1 extends Applet{

    area  obj;               //父类对象

    circle  cir;             //子类对象

    public void init() {

        obj=new area();

        cir=new circle();

    }

    public void paint(Graphics g){ 

        g.drawString("圆面积:"+obj.f(5.0),5,20);

            //调用父类的方法f求圆面积

        g.drawString("圆周长:"+cir.f(5.0),5,40);

//调用子类的方法f求圆周长

        g.drawString("三角形面积:"+cir.g(2.0,8.0),5,60);

4-3  覆盖实现多态性的实例

            //调用所继承父类的方法g求三角形面积

    }

}

PolyMorphism_Example1类的执行结果如图4-3所示。

4.4.3  重载实现多态性

多态性通过重载来实现,也就是说它是在同一个类中定义多个同名方法,这些方法同名的原因是具有相同的功能和目的,但在实现该功能的具体方式和细节方面有所不同,因此需要定义多种不同的实现方法。例如,加法是属于一类操作,目的是把两个数通过“相加”变成一个数。但不同数据类型的加法的具体实现是不一样的,其中差别较大的有整数的加法、复数的加法和分数的加法。利用重载,可以给这些加法都取名为add(x, y),然后再分别定义整数、复数和分数加法的具体实现。

由于重载发生在同一个类中,因此不能再用类名或对象名来区分不同的方法了,在重载中采用的区分方法是使用不同的形式参数表,包括形式参数的个数不同、类型不同或顺序不同。例如,在加法中,整数加法的形参类型是整型,复数加法的形参类型是复数型。在具体实施加法时,尽管调用的是同一个方法名,但根据填入的参数的类型或者参数的个数等的不同,系统可以确定调用哪一个加法函数来完成加法运算。这样,程序运行时只需要使用不同的参数调用add(x, y),即可实现不同类型的加法运算。

【例4-8普通电话的计费方式通常包括在节假日和晚上某个时间段打折,也就是说在普通电话类中就至少包含两种计费方式。因此,可定义两个同名的不同charge_Mode( )方法,在不同情况下调用不同的方法。为了简单起见,设置一个布尔变量discount_timediscount_time为真时打折计费。重载charge_Mode()方法的程序如下:

abstract class Telephone{

    long phoneNumber;

    final int local_Call=1;

    final int distance_Call=3;

    final int international_Call=9;

    double balance;

    abstract boolean charge_Mode (int call_Mode ) ;

    double getBalance ( ) {

         return balance ;

    }

}

class Mobile_Phone extends Telephone{

    String networkType;

    String getType( ) {

        return networkType ;

    }

    boolean charge_Mode (int call_Mode) {

        if(balance > 0.6) {

            balance -= 0.6;

            return true ;

        } else

            return false ;

    }

}

abstract class Fixed_Telephone extends Telephone {

    int monthFee ;

}

class Ordinary_phone extends Fixed_Telephone{

    boolean longdistanceService;

    boolean internationalService;

    boolean charge_Mode (int call_Mode ) {

        if(call_Mode==distance_Call

            &&!longdistanceService) return false;

        if(call_Mode ==international_Call

            &&!internationalService) return false;

                 //检查国内长途和国际长途服务是否开通

        if(balance > (0.2 * call_Mode) ) {

            balance -= (0.2 * call_Mode) ;

            return true;

        } else

            return  false;

    }

    boolean charge_Mode (int call_Mode, boolean discount_time ){

        if (discount_time)                   //如果是在打折的时间

            return  charge_Mode(call_Mode/2);

        else

            return  charge_Mode(call_Mode);   //正常时间的计费方式

        }

}

public  class  ReloadExample{        //主类定义

    public static void main(String args[]){

        Ordinary_phone myOfficePhone=new Ordinary_phone();

        myOfficePhone.internationalService=true;

        myOfficePhone.balance=500.0 ;

        if (myOfficePhone.charge_Mode (9,true))

                    //调用第二个charge_Mode方法

        System.out.println("半价计费方式后剩余金额为:"

            +myOfficePhone.getBalance());

        if (myOfficePhone.charge_Mode (9) )

                    //调用第一个charge_Mode方法

        System.out.println("一般计费方式后剩余金额为:"

4-4  两种计费方式计费后的余额

 

            +myOfficePhone.getBalance()) ;

    }

}

程序运行结果如图4-4所示。

仔细分析这个程序,会发现一个有趣的现象是:在Ordinary_phone类中定义的charge_Mode()方法实现了其父类的同名抽象方法,接着又定义了一个同名方法重载这个方法。