多态性是面向对象程序设计的又一个重要的技术和手段。多态性是指同名的不同方法在程序中共存。即为同一个方法定义几个版本,运行时根据不同情况执行不同的版本。调用者只需使用同一个方法名,系统会根据不同情况,调用相应的不同方法,从而实现不同的功能。多态性即“一个名字,多个方法”。
在面向过程的程序设计中,各函数是不能重名的,否则在使用名字调用时就会产生歧义和错误;而在面向对象的程序设计中,有时却需要利用这样的“重名”现象来提高程序的抽象度和简洁性。
图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 覆盖实现多态性的实例 |
}
}
PolyMorphism_Example1类的执行结果如图4-3所示。
多态性通过重载来实现,也就是说它是在同一个类中定义多个同名方法,这些方法同名的原因是具有相同的功能和目的,但在实现该功能的具体方式和细节方面有所不同,因此需要定义多种不同的实现方法。例如,加法是属于一类操作,目的是把两个数通过“相加”变成一个数。但不同数据类型的加法的具体实现是不一样的,其中差别较大的有整数的加法、复数的加法和分数的加法。利用重载,可以给这些加法都取名为add(x, y),然后再分别定义整数、复数和分数加法的具体实现。
由于重载发生在同一个类中,因此不能再用类名或对象名来区分不同的方法了,在重载中采用的区分方法是使用不同的形式参数表,包括形式参数的个数不同、类型不同或顺序不同。例如,在加法中,整数加法的形参类型是整型,复数加法的形参类型是复数型。在具体实施加法时,尽管调用的是同一个方法名,但根据填入的参数的类型或者参数的个数等的不同,系统可以确定调用哪一个加法函数来完成加法运算。这样,程序运行时只需要使用不同的参数调用add(x, y),即可实现不同类型的加法运算。
【例4-8】普通电话的计费方式通常包括在节假日和晚上某个时间段打折,也就是说在普通电话类中就至少包含两种计费方式。因此,可定义两个同名的不同charge_Mode( )方法,在不同情况下调用不同的方法。为了简单起见,设置一个布尔变量discount_time,discount_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 两种计费方式计费后的余额
|
}
}
程序运行结果如图4-4所示。
仔细分析这个程序,会发现一个有趣的现象是:在Ordinary_phone类中定义的charge_Mode()方法实现了其父类的同名抽象方法,接着又定义了一个同名方法重载这个方法。