• 沒有找到結果。

5 动态多态性

在文檔中 第三章 面向对象基本特性 (頁 21-28)

3.5.1  情境描述 

Tom 在该公司进一步调查发现,同样是 B 类员工,有的员工工资计算的时候,计算了岗 位工资,没有时间工资,而另外一部分员工有时间工资,没有岗位工资,为了解决这个问题, 

Tom 需要完成以下任务:

(1)利用基类派生出子类。

(2)重写基类中的工资计算方法。 

3.5.2  问题分析 

B  类员工的工资计算不是简单的拆分问题,故针对  B  类员工而言,其工资的计算应该派 生出两个子类, 分别处理两种不同业务逻辑的工资计算, 在子类中对不带参数的方法 calWages  进行重写可以达到目的。 

3.5.3  解决方案

(1)打开 Eclipse,选择项目 Task3_4,复制粘贴形成新的项目 Task3_5。

(2)修改 SalaryB 的类,去掉非公共成员。 

/** 

*/ 

package com.esms; 

/** 

* @author  李法平 

*/ 

public class SalaryB extends Salary {

private double baseWage; 

private double timeWage; 

/** 

*  构造方法 

* @param obj 

* @param wage 

*/ 

public SalaryB(EmployeeB obj) {  super(obj); 

this.baseWage = 0; 

/** 

*  构造方法 

* @param obj 

* @param wage 

* @param baseWage 

* @param posWage 

* @param ageWage 

* @param timeWage 

*/ 

public SalaryB(EmployeeB obj, double baseWage) {  super(obj); 

this.baseWage = baseWage; 

/** 

* @return the baseWage 

*/ 

public double getBaseWage() {  return baseWage; 

/** 

* @param baseWage 

*      the baseWage to set 

*/ 

public void setBaseWage(double baseWage) {  this.baseWage = baseWage; 

}  /* 

*  工资计算 

* @see com.esms.Salary#calWages()

*/ 

@Override 

public void calWages() { 

this.wage = this.baseWage    ;  } 

/** 

*  具备加班工资的工资计算 

* @param addWage 

*/ 

public void calWages(double addWage) {  calWages(); 

this.wage = this.wage + addWage; 

}  } 

(3)新建 SalaryB 的派生类 SalaryPostB 及 SalaryTimeB 两个子类,具体如图 3­8 所示。

图 3­8  新建 SalaryPostB 

新建成功之后,分别修改两个类的构造方法,适应新的构造。修改 SalaryPostB 类的构造 方法。 

/** 

*/ 

package com.esms;

/** 

* @author  李法平 

*/ 

public class SalaryPostB extends SalaryB {  private double posWage; 

/** 

* @return the posWage 

*/ 

public double getPosWage() {  return posWage; 

/** 

* @param posWage the posWage to set 

*/ 

public void setPosWage(double posWage) {  this.posWage = posWage; 

/** 

* @param obj 

*/ 

public SalaryPostB(EmployeeB obj) {  super(obj); 

//TODO Auto­generated constructor stub  } 

/** 

* @param obj 

* @param baseWage 

* @param posWage 

* @param timeWage 

*/ 

public SalaryPostB(EmployeeB obj, double baseWage, double posWage  ) {

super(obj, baseWage); 

this.posWage=posWage; 

//TODO Auto­generated constructor stub  } 

修改 SalaryTimeB 类的构造方法。 

/** 

*/

package com.esms; 

/** 

* @author  李法平 

*/ 

public class SalaryTimeB extends SalaryB {  double timeWage; 

/** 

* @param obj 

*/ 

public SalaryTimeB(EmployeeB obj) {  super(obj); 

//TODO Auto­generated constructor stub  } 

/** 

* @param obj 

* @param baseWage 

* @param posWage 

* @param timeWage 

*/ 

public SalaryTimeB(EmployeeB obj, double baseWage,  double timeWage) { 

super(obj, baseWage ); 

this.timeWage=timeWage; 

//TODO Auto­generated constructor stub  } 

/** 

* @return the timeWage 

*/ 

public double getTimeWage() {  return timeWage; 

/** 

* @param timeWage the timeWage to set 

*/ 

public void setTimeWage(double timeWage) {  this.timeWage = timeWage; 

/* (non­Javadoc) 

* @see com.esms.SalaryB#calWages() 

*/

@Override 

public void calWages() { 

EmployeeB obj = (EmployeeB) this.empObj; 

this.wage=this.getBaseWage()+this.getTimeWage()*obj.getEmployeeWorkTimes(); 

}  } 

(4)针对 SalaryPostB 类,重写 calWages 方法。 

/* (non­Javadoc) 

* @see com.esms.SalaryB#calWages() 

*/ 

@Override 

public void calWages() { 

this.wage=this.getBaseWage()+this.getPosWage(); 

(5)根据 SalaryTimeB 类的特性,在 SalaryTimeB 中重写 calWages 方法。 

/* (non­Javadoc) 

* @see com.esms.SalaryB#calWages() 

*/ 

@Override 

public void calWages() { 

EmployeeB obj = (EmployeeB) this.empObj; 

this.wage=this.getBaseWage()+ 

this.getTimeWage()*obj.getEmployeeWorkTimes(); 

(6)在 SalaryB 基类中编写主函数验证。 

/** 

*  主方法 

* @param args 

*/ 

public static void main(String[] args) { 

EmployeeB objTom = new EmployeeB();  //创建员工对象  objTom.setEmployeeNo("001");  //设置工号  objTom.setEmployeeName("汤姆");  //设置姓名  SalaryB salaryTom = new SalaryPostB(objTom,1500,2000); 

//工资计算 

salaryTom.calWages(); 

System.out.println("以下是 B 类员工按岗位计算工资情况:"); 

//输出当前员工的工资  salaryTom.print(); 

EmployeeB objJack=new EmployeeB(); 

objJack.setEmployeeNo("002"); 

objJack.setEmployeeName("杰克"); 

objJack.setEmployeeWorkTimes(64); 

//第二个构造方法构造对象

SalaryB salaryJack = new SalaryTimeB(objJack,1000,30); 

salaryJack.calWages(400);//带加班工资的计算 

System.out.println("以下是 B 类员工按小时计算工资情况:"); 

salaryJack.print(); 

(7)运行结果如图 3­9 所示。

图 3­9  程序运行结果 

3.5.4  知识总结  1.方法的覆盖

方法覆盖是 Java 实现多态性机制的另一种方式。在类的继承的部分已经作了介绍,由于 它在面向对象的程序设计中的重要性,这里再次对它进行详细的讨论。

在继承关系中,基类(也包括接口)和子类存在同名的方法,如果同时满足以下条件:

l 相同的参数(包括参数个数、类型、顺序)。

l 相同的返回值类型。

那么,子类的方法覆盖基类的方法。使用覆盖时需注意以下几点:

l 不允许出现参数相同,但返回值类型不同的同名方法。

l 子类方法不能比基类同名方法有更严格的访问范围。

l 子类方法不能比基类同名方法抛出更多的异常。

覆盖是一种动态的多态,它是通过基类引用来体现的。JVM  根据当前被引用对象的类型 来动态地决定执行的是覆盖方法的哪个版本。 

2.多态的优点

覆盖这种形式的多态具有以下优点:

(1)可替换性:多态对已存在的代码具有可替换性。

(2)可扩充性:多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继 承性,以及其他特性的运行和操作。

(3)接口性:多态是接口通过方法签名,向子类提供一个共同接口,由子类来覆盖它而 实现的。 

3.5.5  应用实践

为了更充分地理解多态,体现面向对象程序设计的核心思想,完成本实践内容。

修改实践 3.3.5,通过接口实现对圆、矩形求面积。

interface ShapeOver {  double area(); 

class RectangleOver implements ShapeOver {  public double length; 

public double width; 

public double area() {  //覆盖接口的方法  return length * width; 

class CircleOver implements ShapeOver {  public double radius; 

public double area() {  //覆盖接口的方法  return Math.PI * radius * radius; 

public class Practise3_5_5 { 

public static void main(String[] args) { 

RectangleOver r = new RectangleOver();  //声明一个矩形  CircleOver c = new CircleOver();  //声明一个圆  r.length = 10; 

r.width = 8; 

c.radius = 5; 

ShapeOver shape; 

shape = r; 

printArea(shape);    //实参是 shape,但它指向的是 RectangleOver()的实例  shape = c; 

printArea(shape);    //实参是 shape,但它指向的是 CircleOver()的实例 

static void printArea(ShapeOver shape) { 

System.out.println("面积是:" + shape.area()); 

//动态多态,根据传入对象的实际类型,调用正确的覆盖方法的版本  //是矩形时,调用矩形的面积计算方法;是圆时,调用圆的面积计算方法  } 

在此基础上,读者可根据 Shape 的接口要求,增加实现三角形类等功能。

在文檔中 第三章 面向对象基本特性 (頁 21-28)

相關文件