PHP中trait的使用和同时引入多个trait时同名方法冲突的处理方法
作者:叶随风 发布时间:2023-06-16 23:13:52
PHP的类是单一继承模式,也就是每个类只能继承一个父类(基类)。
但有时需要引入更多通用(共用)的方法,同时这些方法又不适合集成到基类。
那么这时,就需要使用其他方法来引入这些方法。其中trait,就是方法之一。
trait是PHP5.4之后出现的一种代码复用方法,形式和Class非常相似,同时可以随意组合任意引入。
trait一般在当前类和其同父类(基类)的其他类都需要使用相同方法时,而其父类(基类)又要尽量避免出现这些方法时使用。
甚至有时可能其他关联不是特别大的类(分别继承不同的父类)也可能会使用共同的方法,也可以使用trait的方法。
尽量通俗一点的说一下trait:
trait像类,但不是类,不可以直接使用new关键字创建对象;简单理解是用类的形式,封装一大堆通用(共用)的方法,供其他类引用。
trait和use搭配使用。定义好trait后,“use trait定义的名字;”,就可以直接使用里边定义的一切了,是不是很简单?很方便?
现在知道了trait,接下来就通过代码实例,演示一下trait的具体使用和一些小情况。
一、trait的使用
代码:
// trait
trait traitTest {
public function test() {
echo "trait test...\n";
}
}
// 父类
class ParentClass {
public function parent() {
echo "parent...\n";
}
}
// 子类
class SubClass extends ParentClass {
use traitTest;
public function sub() {
echo "sub...\n";
}
}
$obj = new SubClass;
$obj->sub();// 调用子类方法
$obj->parent();// 调用父类的方法
$obj->test();// 调用trait里的方法
代码和结果截图:
上边的这个例子,子类直接extentds父类,然后又在类内use了trait。这样当前类(子类)就拥有了这三个的全部方法。
子类的sub方法,父类的parent方法,trait的test方法,在子类内都可以直接调用使用。
最基础的使用就这些,看起来是不是也不算难?甚至感觉挺简单的?
那么我们进一步思考一下,类的“继承”难免会出现同名方法,那么这三个里边如果有同名方法,最终会保留哪个?谁的方法会被覆盖呢?
二、当父类、子类和trait的方法重名
代码:
// trait
trait traitTest {
public function test() {
echo "trait test...\n";
}
public function lookClassName() {
echo "trait here\n";
echo __CLASS__ . "\n";
}
}
// 父类
class ParentClass {
public function parent() {
echo "parent...\n";
}
public function lookClassName() {
echo __CLASS__ . "\n";
}
}
// 子类
class SubClass extends ParentClass {
use traitTest;
public function sub() {
echo "sub...\n";
}
public function lookClassName() {
echo __CLASS__ . "\n";
}
}
$obj = new SubClass;
$obj->sub();// 调用子类方法
$obj->parent();// 调用父类的方法
$obj->test();// 调用trait里的方法
$obj->lookClassName();// 调用同名方法
代码和结果截图:
上边这段例子的结果很明显的发现,最终当前类(子类)的方法被调用了,也就是三个里边都有同名方法时,当前类的方法优先。
接下来,注释(删除)当前类的lookClassName()方法。
看上边截图,很明显了,当子类(当前类)没有同名方法,只有父类(基类)和trait中的方法同名时,trait中的方法优先。
结论:当前类(子类)、trait和父类(基类)中有同名方法时“子类高于trait高于父类”。子类的方法会覆盖trait中的方法,而trait的方法会覆盖父类的方法。
前边有提到,trait可以随意组合,随意引用,那么是不是可以同时引入多个trait呢?是。在一个类内,可以同时use多个trait。
三、类内同时引入多个trait
// trait
trait traitTest {
public function test() {
echo "trait test...\n";
}
public function lookClassName() {
echo "trait here\n";
echo __CLASS__ . "\n";
}
}
trait traitTest2 {
public function test2() {
echo "trait2 test...\n";
}
public function lookClassName() {
echo "trait2 here\n";
echo __CLASS__ . "\n";
}
}
trait traitTest3 {
public function test3() {
echo "trait3 test...\n";
}
public function lookClassName() {
echo "trait3 here\n";
echo __CLASS__ . "\n";
}
}
// 父类
class ParentClass {
public function parent() {
echo "parent...\n";
}
public function lookClassName() {
echo __CLASS__ . "\n";
}
}
// 子类
class SubClass extends ParentClass {
use traitTest;
use traitTest2, traitTest3;
public function sub() {
echo "sub...\n";
}
public function lookClassName() {
echo __CLASS__ . "\n";
}
}
$obj = new SubClass;
$obj->sub();// 调用子类方法
$obj->parent();// 调用父类的方法
$obj->test();// 调用trait里的方法
$obj->test2();// 调用trait2里的方法
$obj->test3();// 调用trait3里的方法
$obj->lookClassName();// 调用同名方法
代码和结果截图:
当需要同时引入多个trait时,只要use trait1, trait2, trait3,在use后边跟多个trait名字即可,多个trait之间用逗号分隔。
当然,也可以分开写,每次use一个trait进来。
此时又有新的问题产生了,如果引入的多个trait都有同名的方法,那么又会是谁优先?谁又被覆盖呢?
四、引入多个trait有同名方法
代码:
// trait
trait traitTest {
public function test() {
echo "trait test...\n";
}
public function lookClassName() {
echo "trait here\n";
echo __CLASS__ . "\n";
}
}
trait traitTest2 {
public function test2() {
echo "trait2 test...\n";
}
public function lookClassName() {
echo "trait2 here\n";
echo __CLASS__ . "\n";
}
}
trait traitTest3 {
public function test3() {
echo "trait3 test...\n";
}
public function lookClassName() {
echo "trait3 here\n";
echo __CLASS__ . "\n";
}
}
// 父类
class ParentClass {
public function parent() {
echo "parent...\n";
}
public function lookClassName() {
echo __CLASS__ . "\n";
}
}
// 子类
class SubClass extends ParentClass {
use traitTest, traitTest2, traitTest3 {
traitTest2::lookClassName insteadof traitTest;// traitTest2代替了traitTest
traitTest3::lookClassName insteadof traitTest2;// traitTest3代替了traitTest2
}
public function sub() {
echo "sub...\n";
}
// public function lookClassName() {
// echo __CLASS__ . "\n";
// }
}
$obj = new SubClass;
$obj->sub();// 调用子类方法
$obj->parent();// 调用父类的方法
$obj->test();// 调用trait里的方法
$obj->test2();// 调用trait2里的方法
$obj->test3();// 调用trait3里的方法
$obj->lookClassName();// 调用同名方法
代码和结果截图:
说明(上边的源码和结果是解冲突之后的):
当子类没有(注释或者删除)lookClassName()方法时,调用lookClassName方法,则会调用trait中的方法,因为三个trait中都有同名方法,此时就会发生致命错误(冲突)。
报下边(看截图)的语法错误
此时,就需要解冲突。
解冲突,就需要使用到insteadof关键字,含义是“代替”,就是用哪个代替哪个。
use traitTest, traitTest2, traitTest3 {
traitTest2::lookClassName insteadof traitTest;// traitTest2代替了traitTest
traitTest3::lookClassName insteadof traitTest2;// traitTest3代替了traitTest2
}
解引入多个trait多个重名方法冲突时,需要在引入时使用insteadof关键字,逐一说明哪个trait的方法代替了哪个trait的(看上边引入代码的注释)。
根据上边引入的代码,是traitTest2的lookClassName代替了traitTest的,然后traitTest3的代替了traitTest2的。
因此,最终输出结果时,调用lookClassName(),输出的就是traitTest3的内容(输出结果看上边最近的“代码和结果截图”)。
当然,也可以换个写法:
use traitTest, traitTest2, traitTest3 {
traitTest2::lookClassName insteadof traitTest3;// traitTest2代替了traitTest3
traitTest3::lookClassName insteadof traitTest2;// traitTest3代替了traitTest2
}
这个写法呢,是traitTest2和traitTest3互相代替了,那么此时反而没有traitTest什么事了。这个时候,再调用lookClassName()方法,输出的就是traitTest的lookClassName()方法的内容。
代码和结果截图:
如图,当traitTest2和traitTest3互相代替后,直接输出了traitTest的内容。
到这基本就该结束了,但,有个特殊情况需要考虑一下。
我们之所以会引入多个trait,说明这几个trait里都有想使用的方法,那么非常巧合,其中同名方法正好又都想使用,被代替的方法还能使用么?
五、当引入多个trait,同名方法解冲突后,同时使用所有冲突的同名方法
解决:我们需要使用到另一个关键字“as”,此关键字的功能,简单理解就是给方法取一个别名。
代码:
// trait
trait traitTest {
public function test() {
echo "trait test...\n";
}
public function lookClassName() {
echo "trait here\n";
echo __CLASS__ . "\n";
}
}
trait traitTest2 {
public function test2() {
echo "trait2 test...\n";
}
public function lookClassName() {
echo "trait2 here\n";
echo __CLASS__ . "\n";
}
}
trait traitTest3 {
public function test3() {
echo "trait3 test...\n";
}
public function lookClassName() {
echo "trait3 here\n";
echo __CLASS__ . "\n";
}
}
// 父类
class ParentClass {
public function parent() {
echo "parent...\n";
}
public function lookClassName() {
echo __CLASS__ . "\n";
}
}
// 子类
class SubClass extends ParentClass {
use traitTest, traitTest2, traitTest3 {
traitTest2::lookClassName insteadof traitTest3;// traitTest2代替了traitTest3
traitTest3::lookClassName insteadof traitTest2;// traitTest3代替了traitTest2
traitTest2::lookClassName as lookClassName2;// traitTest2的lookClassName改别名lookClassName2
traitTest3::lookClassName as lookClassName3;// traitTest3的lookClassName改别名lookClassName3
}
public function sub() {
echo "sub...\n";
}
// public function lookClassName() {
// echo __CLASS__ . "\n";
// }
}
$obj = new SubClass;
$obj->sub();// 调用子类方法
$obj->parent();// 调用父类的方法
$obj->test();// 调用trait里的方法
$obj->test2();// 调用trait2里的方法
$obj->test3();// 调用trait3里的方法
$obj->lookClassName();// 调用同名方法
$obj->lookClassName2();// 调用traitTest2更名后的同名方法
$obj->lookClassName3();// 调用traitTest3更名后的同名方法
代码和结果截图:
根据上图,就可以看出,当trait2和trait3互相代替,然后同名方法另起别名后,三个trait的同名方法,不再冲突,并且可以分别调用各自原本同名的方法。
到此要说的东西基本都说完了。算是对PHP的trait的一个小小的总结,希望可以帮到需要的朋友。
来源:https://www.cnblogs.com/leafinwind/archive/2023/04/26/17354416.html
猜你喜欢
- 本文实例讲述了python编程开发之类型转换convert。分享给大家供大家参考,具体如下:在python的开发过程中,难免会遇到类型转换,
- re.findall()在字符串中找到正则表达式所匹配的所有子串,并返回一个列表,如果没有找到匹配的,则返回空列表。语法格式:re.find
- super()函数可以用于继承父类的方法,语法如下:super(type[, object-or-type])虽然super()函数的使用比
- 1.BeautifulSoup简介BeautifulSoup4和 lxml 一样,Beautiful Soup 也是一个HTML/XML的解
- 定义临时变量:{% with i=1 %}{{i}}{% endwith %}定义对临时变量操作的tag在templatetags中创建se
- 在不同平面上绘制二维条形图。本实例制作了一个3d图,其中有二维条形图投射到平面y=0,y=1,等。演示结果:完整代码:from mpl_to
- 【名称】Abs【类别】数学函数【原形】Abs(number)【参数】必选的。Number参数是一个任何有效的数值型表达式【返回值】同numb
- 不知各位是否有手写代码的习惯。例如:要在一个单元格插入一段CSS代码,或者一段Javascript代码,怎么做才比较快捷方便呢?虽然Drea
- 接下来,请按照以下步骤操作:完成上述步骤后,您应该能够使用 sa 用户及其密码在程序中连接到 SQL Server Express Loca
- 神经网络梯度下降法在详细了解梯度下降的算法之前,我们先看看相关的一些概念。1. 步长(Learning rate):步长决定了在梯度下降迭代
- 描述符(descriptor)是实现了__get__、__set__、__del__方法的类,进一步可以细分为两类:数据描述符:实现了__g
- 为了减少页面的加载速度,提高用户体验,对于一些图片决定使用图标代替,但是发现element-ui的图标少得可怜,完全满足不了我的要求,于是决
- 在Python的标准库中,functools库中有很多对方法有操作的封装功能,partial Objects就是其中之一,他可以实现对方法参
- 需求是表里的某个字段存储的值是以逗号分隔开来的,要求根据分隔的每一个值都能查出来数据,但是不能使用like查询。数据是这样的:查询的sql如
- 在事务的ACID特性中,原子性(A)、一致性(C)、持久性(D)由undo log和redo log实现,隔离性(I)由锁+MVCC实现un
- 如下所示:import matplotlib.pyplot as pltimport numpy as npimport mathdef g
- 前言 网传的七天学Python的路线如下,我觉得可以在学过此表中前几天的内容后,就可以回头来学习一下列表推导式:它综合了列表、fo
- 一、简介Imageio是一个Python库,提供了一个简单的界面来读取和写入各种图像数据,包括动画图像,视频,体积数据和科学格式。它是跨平台
- 本文实例讲述了PHP实现的AES双向加密解密功能。分享给大家供大家参考,具体如下:<?php/* * Created on 2018-
- 本文实例为大家分享了vue实现登录拦截的具体代码,供大家参考,具体内容如下需求:用户只有登录了,用户名存储在本地储存时,才能进入首页,如果本