5.1.2 多态

    有一个用来判断是否应该设计为继承关系的简单规则,这就是“is-a”规则,它表明子类的每个对象也是超类的对象。例如,每个经理都是雇员,因此,将Manager类设计为Employee类的子类是显而易见的,反之不然,并不是每个雇员都是经理。

    “is-a”规则的另一种表述法是置换法则。它表明程序中出现超类对象的任何地方都可以用子类对象置换。例如,可以将一个子类的对象赋给超类变量。

  
  
  
  
  1. Employee e; 
  2. e = new Employee();    // Employee object expected 
  3. e = new Manager();    // OK, Manager can be used as well 

    在Java程序设计语言中,对象变量是多态的。一个Employee变量既可以引用一个Employee类对象,也可以引用一个Employee类的任何一个子类的对象(例如,Manager,Executive等等)。

    从例5-1中,已经看到了置换法则的优点:

  
  
  
  
  1. Manager boss = new Manager(...); 
  2. Employee[] staff = new Employee[3]; 
  3. staff[0] = boss; 

在这个例子中,变量staff[0]与boss引用同一个对象。但编译器将staff[0]看成Employee对象。

    这意味着,可以这样调用

  
  
  
  
  1. boss.setBonus(5000);    // OK

但不能这样调用

  
  
  
  
  1. staff[0].setBonus(5000);    // Error 

这是因为staff[0]声明的类型是Employee,而setBonus不是Employee类的方法。

    然而,不能将一个超类的引用赋给子类变量。例如,下面的赋值是非法的

  
  
  
  
  1. Manager m = staff[i];    // ERROR 

原因很清楚:不是所有的雇员都是经理。如果赋值成功,m有可能引用了一个不是经理的Employee对象,当在后面调用m.setBonus(...)时就有可能发生运行时错误。

    警告:在Java中,子类数组的引用可以转换成超类数组的引用,而不需要采用强制类型转换。例如,下面是一个经理数组

  
  
  
  
  1. Manager[] managers = new Manager[10]; 

将它转换成Employee[]数组是完全合法的:

  
  
  
  
  1. Employee[] staff = managers;    // OK 

这样做肯定不会有问题,请思考一下其中的缘由。毕竟,如果managers[i]是一个Manager,也一定是一个Employee。然而,实际上,将会发生一些令人惊讶的事情。要切记managers和staff引用的是同一个数组。现在卡一下这条语句:

  
  
  
  
  1. staff[0] = new Employee("Harry Hacker", ...); 

编译器竟然接纳了这个赋值操作。但在这里,staff[0]与managers[0]引用的是同一个对象,似乎我们把一个普通雇员擅自归入经理行列中了。这是一种很忌讳发生的情形,当调用managers[0].setBonus(1000)的时候,将会导致调用一个不存在的实例域,进而搅乱相邻存储空间的内容。

    为了确保不发生这类错误,所有数组都要牢记创建它们的元素类型,并负责监督仅将类型兼容的引用存储到数组中。例如,使用new managers[10]创建的数组时一个经理数组。

    如果试图存储一个Employee类型的引用就会引发ArrayStoreException异常。

你可能感兴趣的:(Java核心技术I)