PHP是单继承的语言,在PHP 5.4 Traits出现之前,PHP的类无法同时从两个基类继承属性或方法。php的Traits和Go语言的组合功能类似,通过在类中使用use关键字声明要组合的Trait名称,而具体某个Trait的声明使用trait关键词,Trait不能直接实例化。具体用法请看下面的代码:

 <?php
trait Drive {
public $carName = 'trait';
public function driving() {
echo "driving {$this->carName}\n";
}
}
class Person {
public function eat() {
echo "eat\n";
}
}
class Student extends Person {
use Drive;
public function study() {
echo "study\n";
}
}
$student = new Student();
$student->study();
$student->eat();
$student->driving();

输出结果如下:

 study
eat
driving trait

上面的例子中,Student类通过继承Person,有了eat方法,通过组合Drive,有了driving方法和属性carName。

如果Trait、基类和本类中都存在某个同名的属性或者方法,最终会保留哪一个呢?通过下面的代码测试一下:

 <?php
trait Drive {
public function hello() {
echo "hello drive\n";
}
public function driving() {
echo "driving from drive\n";
}
}
class Person {
public function hello() {
echo "hello person\n";
}
public function driving() {
echo "driving from person\n";
}
}
class Student extends Person {
use Drive;
public function hello() {
echo "hello student\n";
}
}
$student = new Student();
$student->hello();
$student->driving();

输出结果如下:

 hello student
driving from drive

因此得出结论:当方法或属性同名时,当前类中的方法会覆盖 trait的 方法,而 trait 的方法又覆盖了基类中的方法。

如果要组合多个Trait,通过逗号分隔 Trait名称:

 use Trait1, Trait2;

如果多个Trait中包含同名方法或者属性时,会怎样呢?答案是当组合的多个Trait包含同名属性或者方法时,需要明确声明解决冲突,否则会产生一个致命错误。

 <?php
trait Trait1 {
public function hello() {
echo "Trait1::hello\n";
}
public function hi() {
echo "Trait1::hi\n";
}
}
trait Trait2 {
public function hello() {
echo "Trait2::hello\n";
}
public function hi() {
echo "Trait2::hi\n";
}
}
class Class1 {
use Trait1, Trait2;
}

输出结果如下:

 PHP Fatal error:  Trait method hello has not been applied, because there are collisions with other trait methods on Class1 in ~/php54/trait_3.php on line 20

使用insteadof和as操作符来解决冲突,insteadof是使用某个方法替代另一个,而as是给方法取一个别名,具体用法请看代码:

 <?php
trait Trait1 {
public function hello() {
echo "Trait1::hello\n";
}
public function hi() {
echo "Trait1::hi\n";
}
}
trait Trait2 {
public function hello() {
echo "Trait2::hello\n";
}
public function hi() {
echo "Trait2::hi\n";
}
}
class Class1 {
use Trait1, Trait2 {
Trait2::hello insteadof Trait1;
Trait1::hi insteadof Trait2;
}
}
class Class2 {
use Trait1, Trait2 {
Trait2::hello insteadof Trait1;
Trait1::hi insteadof Trait2;
Trait2::hi as hei;
Trait1::hello as hehe;
}
}
$Obj1 = new Class1();
$Obj1->hello();
$Obj1->hi();
echo "\n";
$Obj2 = new Class2();
$Obj2->hello();
$Obj2->hi();
$Obj2->hei();
$Obj2->hehe();

输出结果如下:

 Trait2::hello
Trait1::hi Trait2::hello
Trait1::hi
Trait2::hi
Trait1::hello

as关键词还有另外一个用途,那就是修改方法的访问控制:

 <?php
trait Hello {
public function hello() {
echo "hello,trait\n";
}
}
class Class1 {
use Hello {
hello as protected;
}
}
class Class2 {
use Hello {
Hello::hello as private hi;
}
}
$Obj1 = new Class1();
$Obj1->hello(); # 报致命错误,因为hello方法被修改成受保护的
$Obj2 = new Class2();
$Obj2->hello(); # 原来的hello方法仍然是公共的
$Obj2->hi(); # 报致命错误,因为别名hi方法被修改成私有的

Trait 也能组合Trait,Trait中支持抽象方法、静态属性及静态方法,测试代码如下:

 <?php
trait Hello {
public function sayHello() {
echo "Hello\n";
}
}
trait World {
use Hello;
public function sayWorld() {
echo "World\n";
}
abstract public function getWorld();
public function inc() {
static $c = 0;
$c = $c + 1;
echo "$c\n";
}
public static function doSomething() {
echo "Doing something\n";
}
}
class HelloWorld {
use World;
public function getWorld() {
return 'get World';
}
}
$Obj = new HelloWorld();
$Obj->sayHello();
$Obj->sayWorld();
echo $Obj->getWorld() . "\n";
HelloWorld::doSomething();
$Obj->inc();
$Obj->inc();

输出结果如下:

 Hello
World
get World
Doing something
1
2

转载自:http://tabalt.net/blog/php-traits/

php之trait-实现多继承的更多相关文章

  1. Scala学习教程笔记二之函数式编程、Object对象、伴生对象、继承、Trait、

    1:Scala之函数式编程学习笔记: :Scala函数式编程学习: 1.1:Scala定义一个简单的类,包含field以及方法,创建类的对象,并且调用其方法: class User { private ...

  2. trait技术详解,这次包你学得会

    trait的使用技巧trait是php5.4以后新增加的一个功能,可以将多个类中,共用的一些属性和方法提取出来做来公共trait类,就像是装配汽车的配件,如果你的类中要用到这些配件,就直接用use导入 ...

  3. Scala入门系列(八):面向对象之trait

    基础知识 1 将trait作为接口使用 此时Trait就与Java中的接口非常类似,不过注意,在Scala中无论继承还是trait,统一都是extends关键字. Scala跟Java 8前一样不支持 ...

  4. php中trait的使用

    1.php中的trait是啥? 看上去既像类又像接口,其实都不是,Trait可以看做类的部分实现,可以混入一个或多个现有的PHP类中,其作用有两个:表明类可以做什么:提供模块化实现.Trait是一种代 ...

  5. Spark基础-scala学习(三、Trait)

    面向对象编程之Trait trait基础知识 将trait作为接口使用 在trait中定义具体方法 在trait中定义具体字段 在trait中定义抽象字段 trait高级知识 为实例对象混入trait ...

  6. PHP类多继承的替代方案Traits

    概述 traits是PHP5.4新进入的特性,其目的就是解决PHP的类不能多继承的问题.Traits不是类!不能被实例化.可以理解为一组能被不同的类都能调用到的方法集合.只需要在类中使用关键词use引 ...

  7. 【Scala学习之二】 Scala 集合 Trait Actor

    环境 虚拟机:VMware 10 Linux版本:CentOS-6.5-x86_64 客户端:Xshell4 FTP:Xftp4 jdk1.8 scala-2.10.4(依赖jdk1.8) spark ...

  8. php面向对象之trait

    trait的使用技巧trait是php5.4以后新增加的一个功能,可以将多个类中,共用的一些属性和方法提取出来做来公共trait类,就像是装配汽车的配件,如果你的类中要用到这些配件,就直接用use导入 ...

  9. scala学习笔记-面向对象编程之Trait

    将trait作为接口使用 1 // Scala中的Triat是一种特殊的概念 2 // 首先我们可以将Trait作为接口来使用,此时的Triat就与Java中的接口非常类似 3 // 在triat中可 ...

  10. php 类继承

    %token T_EXTENDS "extends (T_EXTENDS)" unticked_class_declaration_statement: class_entry_t ...

随机推荐

  1. UVA.548 Tree(二叉树 DFS)

    UVA.548 Tree(二叉树 DFS) 题意分析 给出一棵树的中序遍历和后序遍历,从所有叶子节点中找到一个使得其到根节点的权值最小.若有多个,输出叶子节点本身权值小的那个节点. 先递归建树,然后D ...

  2. Linux环境下用Weblogic发布项目【三】 -- 启动、登陆、停止WebLogic

    一.启动WebLogic: 1.启动前,修改访问端口.IP地址方法: 在config.xml中修改,具体路径如下: /root/Oracle/Middleware/user_projects/doma ...

  3. ACE反应器(Reactor)模式(4)

    转载于:http://www.cnblogs.com/TianFang/archive/2006/12/18/596012.html 定时器的实现 通过Reactor机制,还可以很容易的实现定时器的功 ...

  4. bzoj 4069 [Apio2015]巴厘岛的雕塑 dp

    [Apio2015]巴厘岛的雕塑 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 494  Solved: 238[Submit][Status][Dis ...

  5. X210(s5pv210)中断系统

    1.SoC对中断的实现机制:异常向量表 (1)异常向量表是CPU中某些特定地址的特定定义.当中断发生的时候,中断要想办法通知CPU去处理中断,怎么做到?这就要靠异常向量表.(2)在CPU设计时,就事先 ...

  6. Idrac6 to manage dell server

    最近idrac6挂了,java已经升级了 1.安装firefox浏览器,只有火狐是支持idrac最好的 2.安装JDK 3.配置configure java, 4.添加security,edit si ...

  7. bzoj 1594: [Usaco2008 Jan]猜数游戏——二分+线段树

    Description 为了提高自己低得可怜的智商,奶牛们设计了一个新的猜数游戏,来锻炼她们的逻辑推理能力. 游戏开始前,一头指定的奶牛会在牛棚后面摆N(1 <= N<= 1,000,00 ...

  8. 【NOIP】提高组2016 愤怒的小鸟

    [题意]Universal Online Judge [算法]状态压缩型DP [题解]看数据范围大概能猜到是状压了. 根据三点确定一条抛物线,枚举两个点之间的抛物线,再枚举有多少点在抛物线上(压缩为状 ...

  9. 【TYVJ】P1039 忠诚2

    [算法]线段树 [注意]修改或查询区间时,若区间能包含某棵子树就立即返回,否则线段树就失去了意义. #include<cstdio> #include<algorithm> u ...

  10. C# 关于调用微信接口的代码

    调用微信接口前需要准备的内容. 1.微信公众平台的appid 2.微信公众平台的secret 3..获取tokenid 4.获取ticket 5.生成签名的随机串 6.生成签名的时间戳 7.生成签名 ...