PHP面向对象编程之深入理解方法重载与方法覆盖(多态)


Posted in PHP onDecember 24, 2015

什么是多态?

多态(Polymorphism)按字面的意思就是“多种状态”。在面向对象语言中,接口的多种不同的实现方式即为多态。引用Charlie Calverts对多态的描述——多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作(摘自“Delphi4编程技术内幕”)。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针(没错这段话来自百度百科)。那么多态的作用是什么,它有什么实际开发价值呢?在实际的应用开发中,采用面向对象中的多态主要在于可以将不同的子类对象都当作一个父类来处理,并且可以屏蔽不同子类对象之间所存在的差异,写出通用的代码,做出通用的编程,以适应需求的不断变化。

下面就是PHP中多态的两个实现 

方法重载(overload)

重载是类的多态的一种实现。函数重载指一个标识符被用作多个函数名,且能够通过函数的参数个数或参数类型将这些同名的函数区分开来,调用不发生混淆。即当调用的时候,虽然方法名字相同,但根据参数的不同可以自动调用相应的函数。

class A{
  public function test(){
    echo "test1";
  }
  public function test($a){
    echo "test2";
  }
}
$a=new A();
$a->test();
$a->test($a);

假如php直接支持方法重载的话。那么上面的例子执行后传参和不传参就会返回不同的值。然而php并不直接支持重载,这就意味着你如果直接按上面这样定义的话,就会报错的。会报什么错呢?会报如下的错误。

 PHP面向对象编程之深入理解方法重载与方法覆盖(多态)

这意思就是不能重复定义A函数,报错的行数也正是下面这行。

public function test($a){

所以说php是并不直接支持重载的。合着说了这么半天php并不支持。。别急,我说的是并不直接支持,所以说是我们可以让php间接支持。这时候就要用到一个函数来支持重载了。就是__call()。__call()方法必须带有两个参数。第一个包含了被调用的方法名称,而第二个参数包含了传递给该方法的参数数组。可以通过这个方法实现类似于函数重载的功能。看下面的代码。

public function __call($method,$p)
{
  if($method=="display"){
    if(is_object($p[0])){
      $this->displayObject($p[0]);
    }else if(is_array($p[0])){
      $this->displayArray($p[0]);
    }else{
      $this->displayScalar($p[0]);
    }
  }
}
//下面是对上面定义的调用
$ov=new overload;
$ov->display(array(1,2,3));
$ov->display('cat');

定义方法的时候,可以看到有三个分支,如果一个对象传递给display()方法,就调用的是displayObject()方法;如果传递的是一个数组,调用displayArray();传递的是其他的内容的话,则调用的是displayScalar()方法。。。可以看到下面调用时,第一个是传递了一个数组,则调用displayArray()。第二个传入的不是对象也不是数组,则属于其他内容,调用的是displayScalar()方法。所以这样子就用__call()方法实现了类似于其他语言的方法重载。

方法覆盖(override)

所谓覆盖,从本质上来说就是重写。就是当子类继承父类的一些方法后,子类又在其内部定义了相同的方法,则这个新定义的方法会覆盖继承而来的父类的方法,子类只能调用其内部定义的方法。

有以下几点要求:

1.当一个父类和子类有一个方法,参数和名字完全一致,那么子类方法会覆盖父类的方法。

2.在实行方法覆盖的时候,访问修饰符可以是不一样的,但是子类的访问范围必须大于等于父类的访问范围。

3.要求参数和名字一样。并不是要求子类,父类名称相同。

下面是对这几点的解释:

第一点,必须参数一致,才会实现方法覆盖。当参数个数不一致,则会报错(这就牵扯到上面说所得方法重载)。当方法名字不一致,就不会覆盖,只是子类新定义的方法。;

第二点,这是php这些语言设计时的规定吧。我是这么理解的是访问高一层的东西比较容易,如果再去访问底层的东西权限肯定要高一些。

看代码:

class people{
  protected function sing(){
    echo "人唱歌";
  }
} 
class woman extends people{
  public function sing(){
    echo "女人唱歌";
  }
}
$woman1=new woman();
$woman1->sing();

这样很正常的可以输出“女人唱歌”。但当把woman里的sing()方法改为proctcted,父元素改成public()时,即将父类的访问权限设置的大于子类后,就会报下面的错误。

 PHP面向对象编程之深入理解方法重载与方法覆盖(多态)

 第三点,是要求参数和名字一样,具体就是要求参数的个数与父类相同,而并不是参数名称一致。即传递的参数名字可以为任意,只要保证传递的个数相同即可。

以上内容简单介绍了PHP语言中多态的两个实现。

PS:重写、覆盖、重载、多态几个概念的区别分析

override->重写(=覆盖)、overload->重载、polymorphism -> 多态

override是重写(覆盖)了一个方法,以实现不同的功能。一般是用于子类在继承父类时,重写(重新实现)父类中的方法。
重写(覆盖)的规则:

   1、重写方法的参数列表必须完全与被重写的方法的相同,否则不能称其为重写而是重载.
   2、重写方法的访问修饰符一定要大于被重写方法的访问修饰符(public>protected>default>private)。
   3、重写的方法的返回值必须和被重写的方法的返回一致;
   4、重写的方法所抛出的异常必须和被重写方法的所抛出的异常一致,或者是其子类;
   5、被重写的方法不能为private,否则在其子类中只是新定义了一个方法,并没有对其进行重写。
   6、静态方法不能被重写为非静态的方法(会编译出错)。

overload是重载,一般是用于在一个类内实现若干重载的方法,这些方法的名称相同而参数形式不同。

重载的规则:

   1、在使用重载时只能通过相同的方法名、不同的参数形式实现。不同的参数类型可以是不同的参数类型,不同的参数个数,不同的参数顺序(参数类型必须不一样);
   2、不能通过访问权限、返回类型、抛出的异常进行重载;
   3、方法的异常类型和数目不会对重载造成影响;

多态的概念比较复杂,有多种意义的多态,一个有趣但不严谨的说法是:继承是子类使用父类的方法,而多态则是父类使用子类的方法。

一般,我们使用多态是为了避免在父类里大量重载引起代码臃肿且难于维护。

举个例子:

public class Shape 
{
  public static void main(String[] args){
   Triangle tri = new Triangle();
   System.out.println("Triangle is a type of shape? " + tri.isShape());// 继承
   Shape shape = new Triangle();
   System.out.println("My shape has " + shape.getSides() + " sides."); // 多态
   Rectangle Rec = new Rectangle();
   Shape shape2 = Rec;
   System.out.println("My shape has " + shape2.getSides(Rec) + " sides."); //重载
  }
  public boolean isShape(){
   return true;
  }
  public int getSides(){
   return 0 ;
  }
  public int getSides(Triangle tri){ //重载
   return 3 ;
  }
  public int getSides(Rectangle rec){ //重载
  return 4 ;
  }
}
class Triangle extends Shape 
{
  public int getSides() { //重写,实现多态
   return 3;
  }
}
class Rectangle extends Shape 
{
  public int getSides(int i) { //重载
  return i;
  }
}

注意Triangle类的方法是重写,而Rectangle类的方法是重载。对两者比较,可以发现多态对重载的优点:

如果用重载,则在父类里要对应每一个子类都重载一个取得边数的方法;

如果用多态,则父类只提供取得边数的接口,至于取得哪个形状的边数,怎样取得,在子类里各自实现(重写)。

PHP 相关文章推荐
php设计模式 Decorator(装饰模式)
Jun 26 PHP
php图片加水印原理(超简单的实例代码)
Jan 18 PHP
使用php get_headers 判断URL是否有效的解决办法
Apr 27 PHP
从零开始学YII2框架(四)扩展插件yii2-kartikgii
Aug 20 PHP
destoon调用discuz论坛中带图片帖子的实现方法
Aug 21 PHP
Laravel实现用户注册和登录
Jan 23 PHP
PHP实现全角字符转为半角方法汇总
Jul 09 PHP
CodeIgniter基于Email类发邮件的方法
Mar 29 PHP
PHP读书笔记_运算符详解
Jul 01 PHP
PHP7常量数组用法分析
Sep 26 PHP
PHP读取大文件的几种方法介绍
Oct 27 PHP
Yii CFileCache 获取不到值的原因分析
Feb 08 PHP
盘点PHP和ASP.NET的10大对比!
Dec 24 #PHP
php采用session实现防止页面重复刷新
Dec 24 #PHP
Linux+Nginx+MySQL下配置论坛程序Discuz的基本教程
Dec 23 #PHP
分享PHP计算两个日期相差天数的代码
Dec 23 #PHP
php获得客户端浏览器名称及版本的方法(基于ECShop函数)
Dec 23 #PHP
PHP+MySQL实现无极限分类栏目的方法
Dec 23 #PHP
PHP多维数组转一维数组的简单实现方法
Dec 23 #PHP
You might like
不用iconv库的gb2312与utf-8的互换函数
2006/10/09 PHP
php简单对象与数组的转换函数代码(php多层数组和对象的转换)
2011/05/18 PHP
php预定义变量使用帮助(带实例)
2013/10/30 PHP
基础的prototype.js常用函数及其用法
2007/03/10 Javascript
关于onScroll事件在IE6下每次滚动触发三次bug说明
2011/09/21 Javascript
jQuery lazyLoad图片延迟加载插件的优化改造方法分享
2013/08/13 Javascript
$.each遍历对象、数组的属性值并进行处理
2014/07/18 Javascript
JQuery中serialize() 序列化
2015/03/13 Javascript
javascript验证邮件地址和MX记录的方法
2015/06/16 Javascript
Vue.js动态组件解析
2016/09/09 Javascript
浅谈jquery上下滑动的注意事项
2016/10/13 Javascript
工作中常用的js、jquery自定义扩展函数代码片段汇总
2016/12/22 Javascript
vue增删改查的简单操作
2017/07/15 Javascript
浅谈Vue.js 关于页面加载完成后执行一个方法的问题
2019/04/01 Javascript
javascript中this的用法实践分析
2019/07/29 Javascript
vue 中 elment-ui table合并上下两行相同数据单元格
2019/12/26 Javascript
vue 实现element-ui中的加载中状态
2020/11/11 Javascript
[01:06:12]VP vs NIP 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/17 DOTA
深入理解python函数递归和生成器
2016/06/06 Python
全面了解Python环境配置及项目建立
2016/06/30 Python
Python3实现转换Image图片格式
2018/06/21 Python
python dataframe向下向上填充,fillna和ffill的方法
2018/11/28 Python
python3实现点餐系统
2019/01/24 Python
详解Python3中ceil()函数用法
2019/02/19 Python
详细介绍pandas的DataFrame的append方法使用
2019/07/31 Python
Python 写入训练日志文件并控制台输出解析
2019/08/13 Python
PyCharm License Activation激活码失效问题的解决方法(图文详解)
2020/03/12 Python
使用html5 canvas绘制圆环动效
2019/06/03 HTML / CSS
英国鞋类及配饰零售商:Kurt Geiger
2017/02/04 全球购物
英国异国风情旅游网站:Travel Talk Tours(团体旅游、探险旅游、帆船假期)
2018/07/26 全球购物
农民工工资发放承诺书
2014/03/31 职场文书
2014年无财产无子女离婚协议书范本
2014/10/09 职场文书
入党个人总结范文
2015/03/02 职场文书
Nginx本地目录映射实现代码实例
2021/03/31 Servers
python通过函数名调用函数的几种方法总结
2021/06/07 Python
Li list-style-image 图片垂直居中实现方法
2023/05/21 HTML / CSS