先简单说一下多态
多态的三大特征:
- 子类继承父类
- 子类重写父类
- 父类指向子类
多态实现的前提:必须是类与类之间要有关系,要么继承,要么实现,存在重写(override),其实就是抽象函数或接口。
多态的应用:父类对象的引用指向子类对象,其实本质上就是一个向上转型。
举个模型例子,一家公司有员工类(Employee),还有其子类:销售(Sales)、市场(Market)、工程师(Engineer)等。某一天老板招待所有的员工开了个短会,完了之后对所有的员工(Employee)说,大家回去工作吧。在这里我们可以认为老板调用了所有员工对象的continueToWork()方法,而不是对一个个员工细说做什么工作,比如对销售部说你去制定销售计划(调用makeSalePlan();),对市场部说你去制定产品的价格(调用makeProductPrice();)…这种逐个细说的方式并不是面向对象,而是面向个体。可以确定的是,员工类应该有一个方法continueToWork()。而员工如何实现他们工作的方法却只有精确到子类才能确定,因为不同的员工的工作方式是不一样的。因此,我们很多时候只需要关心对象的父类型,而忽略更精确的子类型,比如上面老板叫大家回去工作时,他对全体员工说的,主要指的是全体员工类型。
上述的UML图:
多态的好处:大大提高程序的扩展,增强系统的灵活性,降低模块间的耦合。
具体Java代码实现如下:
abstract class Employee{
abstract void continueToWork();
}
class Sales extends Employee{
private void makeSalePlan(){
System.out.println("make sale plan");
}
public void continueToWork(){
makeSalePlan();
}
}
class Market extends Employee{
private void makeProductPrice(){
System.out.println("make product price");
}
public void continueToWork(){
makeProductPrice();
}
}
class Engineer extends Employee{
private void makeNewProduct(){
System.out.println("make new product");
}
public void continueToWork(){
makeNewProduct();
}
}
错误的调用示范:
class Demo{
public static void main(String[] args){
Sales s = new Sales();
s.continueToWork();
Market m = new Market();
m.continueToWork();
Engineer e = new Engineer();
e.continueToWork();
}
}
以上的这种调用并不能称为多态,虽然看起来都通过调用continueToWork()来得到结果,但是并不是通过同一接口来实现的,这只是不同类的对象都有相同名称的接口方法,用上述模型来说,就是老板单独对销售部说你去工作,单独对市场部说你去工作,单独对工程师说你去工作,而不是对所有的员工说。
正确的调用方式:
class Demo{
public static void main(String[] args){
Work(new Sales());//Employee e = new Sales();
Work(new Market());//Employee e = new Market();
Work(new Engineer());//Employee e = new Engineer();
}
public static void Work(Employee e){
e.continueToWork();
}
}
上面通过调用统一的接口Work()来工作,这种调用才是多态。
还可以利用重载来实现伪多态:(注:本作者认为重载不属于多态的范畴,但是重载这种语言特性是可以帮助我们来实现伪多态的,具体请见本人的另一篇博文:《重载不应归在多态的范畴内》)
class Demo{
public static void main(String[] args){
Work(new Sales());
Work(new Market());
Work(new Engineer());
}
public static void Work(Sales s){
s.continueToWork();
}
public static void Work(Market m){
m.continueToWork();
}
public static void Work(Engineer e){
e.continueToWork();
}
}
为什么我称上面调用是伪多态呢?因为上述的这种方式,细究其实跟第一种错误调用方式是一样的,只不过利用了重载加以封装,使得看起来是通过统一的接口Work()来工作,但其实它并没有让父类对象的引用指向子类对象。
最后附上php代码实现多态:
<?php
abstract class Employee{
abstract function continueToWork();
}
class Sales extends Employee{
private function makeSalePlan(){
echo "make sale plan";
}
public function continueToWork(){
$this->makeSalePlan();
}
}
class Market extends Employee{
private function makeProductPrice(){
echo "make product price";
}
public function continueToWork(){
$this->makeProductPrice();
}
}
class Engineer extends Employee{
private function makeNewProduct(){
echo "make new product";
}
public function continueToWork(){
$this->makeNewProduct();
}
}
class Demo{
public function Work(Employee $employeeObj){//添加父类类型限制传参类型,使其满足多态第三点要求,父类指向子类
$employeeObj->continueToWork();
}
}
//调用
$obj = new Demo();
$obj->Work(new Sales());
$obj->Work(new Market());
$obj->Work(new Engineer());
?>
参考资料:
CSDN帖子:重载是不是多态?重载是不是面向对象?
Qi_Yuan的博文《java之多态的使用》
感谢wonderwiller618对本文错误的纠正