PHP中的“重载”是个啥?
很多面试官在面试的时候都会问一些面向对象的问题,面向对象的三大特性中,多态最主要的实现方式就是方法的重载和重写。但是在PHP中,只有重写,并没有完全的重载能力的实现。
重写,子类重写父类方法。
// 重写
class A
{
public function test($a)
{
echo 'This is A:' . $a, PHP_EOL;
}
}
class childA extends A
{
public function test($a)
{
echo 'This is A child:' . $a, PHP_EOL;
}
}
$ca = new childA();
$ca->test(1);
这个在PHP中是没有任何问题的,子类可以重写父类的方法。当实例化子类的时候,调用的就是子类实现的重写的方法。
重载,相同方法名但参数数量或者类型不同。
class A{
function foo($a){
echo $a;
}
// Fatal error: Cannot redeclare A::foo()
function foo($a, $b){
echo $a+$b;
}
}
抱歉,这样写的结果将会是直接的报错。PHP并不支持这样的重载能力。而在PHP的官方手册上,重载的定义是使用__set()、__get()、__call()、__callStatic()等魔术方法来对无法访问的变量或方法进行重载。这与我们所学习的面向对象中的重载完全不同,在手册中的note里也有很多人对此提出了疑问。当然,我们今天并不会再去讲这些魔术方法的使用。关于它们的使用可以参考我们之前写过的文章:PHP中的那些魔术方法(一)、PHP的那些魔术方法(二)
那么,在PHP中可以实现重载吗?当然可以,只不过会麻烦一些:
// 重载
class B
{
public function foo(...$args)
{
if (count($args) == 2) {
$this->fooAdd(...$args);
} else if (count($args) == 1) {
echo $args[0], PHP_EOL;
} else {
echo 'other';
}
}
private function fooAdd($a, $b)
{
echo $a + $b, PHP_EOL;
}
}
$b = new B();
$b->foo(1);
$b->foo(1, 2);
使用一个方法来调用其他方法,根据参数数量来进行判断,就可以实现参数数量不同的方法重载。
// 使用__call()进行重载
class C
{
public function __call($name, $args)
{
if ($name == 'foo') {
$funcIndex = count($args);
if (method_exists($this, 'foo' . $funcIndex)) {
return $this->{'foo' . $funcIndex}(...$args);
}
}
}
private function foo1($a)
{
echo $a, PHP_EOL;
}
private function foo2($a, $b)
{
echo $a + $b, PHP_EOL;
}
private function foo3($a, $b, $c)
{
echo $a + $b + $c, PHP_EOL;
}
}
$c = new C();
$c->foo(1);
$c->foo(1, 2);
$c->foo(1, 2, 3);
使用__call()魔术方法或许会更简单,但也会让一些新手在接手项目的时候蒙圈。毕竟魔术方法对IDE是不友好的,这样的开发让__call()成为了一个模板方法,由它来定义操作的算法骨架。我们也可以根据参数类型来模拟重载能力。
// 参数类型不同的重载
class D {
function __call($name, $args){
if($name == 'foo'){
if(is_string($args[0])){
$this->fooString($args[0]);
}else {
$this->fooInt($args[0]);
}
}
}
private function fooInt(int $a){
echo $a . ' is Int', PHP_EOL;
}
private function fooString(string $a){
echo $a . ' is String', PHP_EOL;
}
}
$d = new D();
$d->foo(1);
$d->foo('1');
不管怎么说,用上述方法实现的方法重载都非常麻烦,因为会让某一个方法或者魔术方法非常重,它需要成为一个控制器来根据参数对内部的方法进行调度。更多的情况下,我们应该还是使用不同的方法名然后抽象公共的部分提取成独立的私有内部方法来实现不同方法名的“重载”。毕竟不同的语言还是要掌握它们不同的个性,并且根据这些个性灵活地运用在我们的项目中。
参考文档:
https://www.php.net/manual/zh/language.oop5.overloading.php#77843
===============
关注公众号:【硬核项目经理】获取最新文章
添加微信/QQ好友:【xiaoyuezigonggong/149844827】免费得PHP、项目管理学习资料
知乎、公众号、抖音、头条搜索【硬核项目经理】
B站ID:482780532
PHP中的“重载”是个啥?的更多相关文章
- C++中的重载隐藏覆盖&&JAVA中的重载覆盖&&多态
class 类继承默认是private, struct 默认继承是public C++中的隐藏: 只要派生类中出现和基类一样的函数名,基类中的函数就会被派生类中的函数给隐藏(如果派生类和基类中的函数名 ...
- C++中的重载,隐藏,覆盖,虚函数,多态浅析
直到今日,才发现自己对重载的认识长时间以来都是错误的.幸亏现在得以纠正,真的是恐怖万分,雷人至极.一直以来,我认为重载可以发生在基类和派生类之间,例如: class A { public: void ...
- c++中运算符重载
c++语言中运算符重载都是通过函数来实现的,所以其实质为函数重载,当c++语言原有的一个运算符被重载之后,它原来所具有的语义并没有消失,只相当于针对一个特定的类定义了一个新的运算符. <1> ...
- Delphi中静态方法重载还是覆盖的讨论
Delphi中静态方法重载还是覆盖的讨论 新人学习Delphi的时候,容易搞不懂的一个问题,当子类方法和基类方法同名,并且参数也一样的时候,叫做什么呢?是覆盖,还是重载呢? 答案是隐藏父类方法. 一般 ...
- 关于C++中操作符重载的疑问 :四个运算符=, ->, [], ()不可以重载为全局函数(友员函数)
转载自:http://blog.csdn.net/u014610226/article/details/47679323 以下是对C++中不能重载为友元函数的四个运算符进行了详细的分析介绍,需 ...
- python中函数重载和重写
python 中的重载 在python中,具有重载的思想却没有重载的概念.所以有的人说python这么语言并不支持函数重载,有的人说python具有重载功能.实际上python编程中具有重载的目的缺 ...
- C++ 语言中的重载、内联、缺省参数、隐式转换等机制展现了很多优点
C++ 语言中的重载.内联.缺省参数.隐式转换等机制展现了很多优点,但是这些 优点的背后都隐藏着一些隐患.正如人们的饮食,少食和暴食都不可取,应当恰到好处. 我们要辨证地看待 C++的新机制,应该恰如 ...
- php中的重载以及几个常用的魔术方法示例
在面向对象语言中,有一个很重要的概念——overload,即重载.所谓重载,一般是用于在一个类内实现若干重载的方法,这些方法的名称相同而参数形式不同.但是,在php中,这个概念跟大多面向对象语言中的“ ...
- C++中不可重载5个运算符
C++中不可重载的5个运算符 C++中的大部分运算符都是可以重载的,只有以下5个运算符不可以重载,他们是: 1 .(点运算符)通常用于去对象的成员,但是->(箭头运算符),是可以重载的 2 ...
- ZT c++ 中的重载全局new,delete
c++ 中的重载全局new,delete 分类: c++ 2010-08-06 10:31 116人阅读 评论(1) 收藏 举报 deletec++file编译器语言工作 最近做一个小项目,对c++又 ...
随机推荐
- WPF listbox 实现动态滚轮下拉定位
private ObservableCollection<keymodel> _listlua; public ObservableCollection<keymodel> l ...
- SpringMVC学习06(JSON)
6.Json 6.1 什么是JSON? JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式,目前使用特别广泛. 采用完全独立于编程语言的文本 ...
- rabbitMQ通过@RabbitListener和配置文件XML创建exchange及队列绑定关系
1.@RabbitListener 2.配置文件 <rabbit:fanout-exchange name="fanoutExchange" xmlns="http ...
- Kali 2.0 安装教程
本文适合KALI初学者,将详细介绍Kali Linux 2.0的安装过程. 首先我们到KALI的官网下载镜像,大家可以自己选择下载32或64位的KALI 2.0系统. KALI 官网:https:// ...
- 012 PCIe总线的基础知识
一.PCIe总线的基础知识 与PCI总线不同,PCIe总线使用端到端的连接方式,在一条PCIe链路的两端只能各连接一个设备,这两个设备互为是数据发送端和数据接收端.PCIe总线除了总线链路外,还具有多 ...
- [转]C# 互操作性入门系列(二):使用平台调用调用Win32 函数
传送门 C#互操作系列文章: C# 互操作性入门系列(一):C#中互操作性介绍 C# 互操作性入门系列(二):使用平台调用调用Win32 函数 C# 互操作性入门系列(三):平台调用中的数据封送处理 ...
- HTML <form> 标签的 method 属性
定义和用法 method 属性规定如何发送表单数据(表单数据发送到 action 属性所规定的页面). 表单数据可以作为 URL 变量(method="get")或者 HTTP p ...
- 【spring 注解驱动开发】spring组件注册
尚学堂spring 注解驱动开发学习笔记之 - 组件注册 组件注册 1.@Configuration&@Bean给容器中注册组件 2.@ComponentScan-自动扫描组件&指定扫 ...
- C#基础知识---获取调用者信息
一.概述 C#5.0提供了一种新功能,可以利用特性和可选参数获得调用者的信息.这些特性信息包括CallerLineNumber.CallerFilePath和CallerMemberName. 二.D ...
- Go与接口:实现接口的条件
接口类型变量 Go是强类型语言,你不能将整数值赋值给浮点型变量.同样,也不能将没有实现接口的类型值赋值给接口类型变量. // 1.定义变量是接口类型 var w io.Writer // 2.将具体类 ...