PHP的学习--Traits新特性
在阅读yii2源码的时候接触到了trait,就学习了一下,写下博客记录一下。
自 PHP 5.4.0 起,PHP 实现了代码复用的一个方法,称为 traits。
Traits 是一种为类似 PHP 的单继承语言而准备的代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用方法集。Traits 和类组合的语义是定义了一种方式来减少复杂性,避免传统多继承和混入类(Mixin)相关的典型问题。
Trait 和一个类相似,但仅仅旨在用细粒度和一致的方式来组合功能。Trait 不能通过它自身来实例化。它为传统继承增加了水平特性的组合;也就是说,应用类的成员不需要继承。
Trait 示例
<?php
trait ezcReflectionReturnInfo {
function getReturnType() { /**/ }
function getReturnDescription() { /**/ }
} class ezcReflectionMethod extends ReflectionMethod {
use ezcReflectionReturnInfo;
/* ... */
} class ezcReflectionFunction extends ReflectionFunction {
use ezcReflectionReturnInfo;
/* ... */
}
?>
优先级
从基类继承的成员被 trait 插入的成员所覆盖。优先顺序是来自当前类的成员覆盖了 trait 的方法,而 trait 则覆盖了被继承的方法。
优先顺序示例
<?php
class Base {
public function sayHello() {
echo 'Hello ';
}
} trait SayWorld {
public function sayHello() {
parent::sayHello();
echo 'World!';
}
} class MyHelloWorld extends Base {
use SayWorld;
} $o = new MyHelloWorld();
$o->sayHello();
?>
以上例程会输出:Hello World!
从基类继承的成员被插入的 SayWorld Trait 中的 sayHello 方法所覆盖。其行为 MyHelloWorld 类中定义的方法一致。优先顺序是当前类中的方法会覆盖 trait 方法,而 trait 方法又覆盖了基类中的方法。
另一个优先级顺序的例子
<?php
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
} class TheWorldIsNotEnough {
use HelloWorld;
public function sayHello() {
echo 'Hello Universe!';
}
} $o = new TheWorldIsNotEnough();
$o->sayHello();
?>
以上例程会输出:Hello Universe!
多个 trait
通过逗号分隔,在 use 声明列出多个 trait,可以都插入到一个类中。
多个 trait 的用法的例子
<?php
trait Hello {
public function sayHello() {
echo 'Hello ';
}
} trait World {
public function sayWorld() {
echo 'World';
}
} class MyHelloWorld {
use Hello, World;
public function sayExclamationMark() {
echo '!';
}
} $o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
$o->sayExclamationMark();
?>
以上例程会输出:Hello World!
冲突的解决
如果两个 trait 都插入了一个同名的方法,如果没有明确解决冲突将会产生一个致命错误。
为了解决多个 trait 在同一个类中的命名冲突,需要使用 insteadof 操作符来明确指定使用冲突方法中的哪一个。
以上方式仅允许排除掉其它方法,as 操作符可以将其中一个冲突的方法以另一个名称来引入。
冲突解决的例子
<?php
trait A {
public function smallTalk() {
echo 'a';
}
public function bigTalk() {
echo 'A';
}
} trait B {
public function smallTalk() {
echo 'b';
}
public function bigTalk() {
echo 'B';
}
} class Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
}
} class Aliased_Talker {
use A, B {
B::smallTalk insteadof A;
A::bigTalk insteadof B;
B::bigTalk as talk;
}
}
?>
在本例中 Talker 使用了 trait A 和 B。由于 A 和 B 有冲突的方法,其定义了使用 trait B 中的 smallTalk 以及 trait A 中的 bigTalk。
Aliased_Talker 使用了 as 操作符来定义了 talk 来作为 B 的 bigTalk 的别名。
修改方法的访问控制
使用 as 语法还可以用来调整方法的访问控制。
修改方法的访问控制的例子
<?php
trait HelloWorld {
public function sayHello() {
echo 'Hello World!';
}
} // 修改 sayHello 的访问控制
class MyClass1 {
use HelloWorld { sayHello as protected; }
} // 给方法一个改变了访问控制的别名
// 原版 sayHello 的访问控制则没有发生变化
class MyClass2 {
use HelloWorld { sayHello as private myPrivateHello; }
}
?>
从 trait 来组成 trait
正如类能够使用 trait 一样,其它 trait 也能够使用 trait。在 trait 定义时通过使用一个或多个 trait,它能够组合其它 trait 中的部分或全部成员。
从 trait 来组成 trait的例子
<?php
trait Hello {
public function sayHello() {
echo 'Hello ';
}
} trait World {
public function sayWorld() {
echo 'World!';
}
} trait HelloWorld {
use Hello, World;
} class MyHelloWorld {
use HelloWorld;
} $o = new MyHelloWorld();
$o->sayHello();
$o->sayWorld();
?>
以上例程会输出:Hello World!
Trait 的抽象成员
为了对使用的类施加强制要求,trait 支持抽象方法的使用。
表示通过抽象方法来进行强制要求的例子
<?php
trait Hello {
public function sayHelloWorld() {
echo 'Hello'.$this->getWorld();
}
abstract public function getWorld();
} class MyHelloWorld {
private $world;
use Hello;
public function getWorld() {
return $this->world;
}
public function setWorld($val) {
$this->world = $val;
}
}
?>
Trait 的静态成员
Traits 可以被静态成员静态方法定义。
静态变量的例子
<?php
trait Counter {
public function inc() {
static $c = 0;
$c = $c + 1;
echo "$c\n";
}
} class C1 {
use Counter;
} class C2 {
use Counter;
} $o = new C1(); $o->inc(); // echo 1
$p = new C2(); $p->inc(); // echo 1
?>
静态方法的例子
<?php
trait StaticExample {
public static function doSomething() {
return 'Doing something';
}
} class Example {
use StaticExample;
} Example::doSomething();
?>
静态变量和静态方法的例子
<?php
trait Counter {
public static $c = 0;
public static function inc() {
self::$c = self::$c + 1;
echo self::$c . "\n";
}
} class C1 {
use Counter;
} class C2 {
use Counter;
} C1::inc(); // echo 1
C2::inc(); // echo 1
?>
属性
Trait 同样可以定义属性。
定义属性的例子
<?php
trait PropertiesTrait {
public $x = 1;
} class PropertiesExample {
use PropertiesTrait;
} $example = new PropertiesExample;
$example->x;
?>
如果 trait 定义了一个属性,那类将不能定义同样名称的属性,否则会产生一个错误。如果该属性在类中的定义与在 trait 中的定义兼容(同样的可见性和初始值)则错误的级别是 E_STRICT,否则是一个致命错误。
冲突的例子
<?php
trait PropertiesTrait {
public $same = true;
public $different = false;
} class PropertiesExample {
use PropertiesTrait;
public $same = true; // Strict Standards
public $different = true; // 致命错误
}
?>
Use的不同
不同use的例子
<?php
namespace Foo\Bar;
use Foo\Test; // means \Foo\Test - the initial \ is optional
?> <?php
namespace Foo\Bar;
class SomeClass {
use Foo\Test; // means \Foo\Bar\Foo\Test
}
?>
第一个use是用于 namespace 的 use Foo\Test,找到的是 \Foo\Test,第二个 use 是使用一个trait,找到的是\Foo\Bar\Foo\Test。
__CLASS__和__TRAIT__
__CLASS__ 返回 use trait 的 class name,__TRAIT__返回 trait name
示例如下
<?php
trait TestTrait {
public function testMethod() {
echo "Class: " . __CLASS__ . PHP_EOL;
echo "Trait: " . __TRAIT__ . PHP_EOL;
}
} class BaseClass {
use TestTrait;
} class TestClass extends BaseClass { } $t = new TestClass();
$t->testMethod(); //Class: BaseClass
//Trait: TestTrait
Trait单例
实例如下
<?php
trait singleton {
/**
* private construct, generally defined by using class
*/
//private function __construct() {}
public static function getInstance() {
static $_instance = NULL;
$class = __CLASS__;
return $_instance ?: $_instance = new $class;
}
public function __clone() {
trigger_error('Cloning '.__CLASS__.' is not allowed.',E_USER_ERROR);
}
public function __wakeup() {
trigger_error('Unserializing '.__CLASS__.' is not allowed.',E_USER_ERROR);
}
}
/**
* Example Usage
*/
class foo {
use singleton;
private function __construct() {
$this->name = 'foo';
}
}
class bar {
use singleton;
private function __construct() {
$this->name = 'bar';
}
}
$foo = foo::getInstance();
echo $foo->name;
$bar = bar::getInstance();
echo $bar->name;
调用trait方法
虽然不很明显,但是如果Trait的方法可以被定义为在普通类的静态方法,就可以被调用
实例如下
<?php
trait Foo {
public static function bar() {
return 'baz';
}
} echo Foo::bar(),"\\n";
?>
PHP的学习--Traits新特性的更多相关文章
- 【Java学习笔记之二十八】深入了解Java8新特性
前言: Java8 已经发布很久了,很多报道表明java8 是一次重大的版本升级.在Java Code Geeks上已经有很多介绍Java 8新特性的文章,例如Playing with Java 8 ...
- Java学习之==>Java8 新特性详解
一.简介 Java 8 已经发布很久了,很多报道表明Java 8 是一次重大的版本升级.Java 8是 Java 自 Java 5(发布于2004年)之后的最重要的版本.这个版本包含语言.编译器.库. ...
- JAVA8 十大新特性详解
前言: Java8 已经发布很久了,很多报道表明Java 8 是一次重大的版本升级.在Java Code Geeks上已经有很多介绍Java 8新特性的文章, 例如Playing with Java ...
- [转] Java 8的新特性
简介 毫无疑问,Java 8是Java自Java 5(发布于2004年)之后的最重要的版本.这个版本包含语言.编译器.库.工具和JVM等方面的十多个新特性.在本文中我们将学习这些新特性,并用实际的例子 ...
- Java 8 新特性终极版
声明:本文翻译自Java 8 Features Tutorial – The ULTIMATE Guide,翻译过程中发现并发编程网已经有同学翻译过了:Java 8 特性 – 终极手册,我还是坚持自己 ...
- Java 8的新特性—终极版
作者:杜琪[译] 原文链接:http://www.jianshu.com/p/5b800057f2d8 1. 简介 毫无疑问,Java 8是Java自Java 5(发布于2004年)之后的最重要的版本 ...
- 【新特性】JDK1.8
一.简介 毫无疑问,Java 8是Java自Java 5(发布于2004年)之后的最重要的版本.这个版本包含语言.编译器.库.工具和JVM等方面的十多个新特性.在本文中我们将学习这些新特性,并用实际的 ...
- java8新特性详解(转)
原文链接. 前言: Java 8 已经发布很久了,很多报道表明Java 8 是一次重大的版本升级.在Java Code Geeks上已经有很多介绍Java 8新特性的文章,例如Playing with ...
- Java8新特性【转】
地址:http://ifeve.com/java-8-features-tutorial/ 1.简介 毫无疑问,Java 8是自Java 5(2004年)发布以来Java语言最大的一次版本升级,Ja ...
随机推荐
- XAF How to show custom forms and controls in XAF (Example)
XAF How to show custom forms and controls in XAF (Example) https://www.devexpress.com/Support/Center ...
- UDP发送中文
procedure TForm1.SpeedButton1Click(Sender: TObject); begin udp.Send('localhost', 1234, 'abc123'); // ...
- java 中 finally里面写了return 会发生什么?
boolean test() throws Exception { boolean ret = true; try { int b = 12; int c; for (int i = 1; i > ...
- Ubuntu 14.4 下安装 Tomcat + Solr5.2
okbt.net 网站采用 Lucene.net 3.03 做数据引擎..不是很理想...后来试用了 Solr 后感觉很不错.多点同步.原子更新等很方便..抽空安装学习了下.. 1. ubuntu ...
- 【腾讯Bugly干货分享】iOS黑客技术大揭秘
本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://dev.qq.com/topic/5791da152168f2690e72daa4 “8小时内拼工作,8小时外拼成长 ...
- MySQL数据丢失讨论
原文地址:http://hatemysql.com/tag/sync_binlog/ 1. 概述 很多企业选择MySQL都会担心它的数据丢失问题,从而选择Oracle,但是其实并不十分清楚什么情况下 ...
- jenkins + Git 搭建持续集成环境
持续集成通过自动化构建.自动化测试以及自动化部署加上较高的集成频率保证了开发系统中的问题能迅速被发现和修复,降低了集成失败的风险,使得系统在开发中始终保持在一个稳定健康的集成状态.jenkins是目前 ...
- Web前端技术研究:Css hack技术---令人沮丧的技术
我最近想好好整理下csshack技术,但是结果很沮丧,下面我将我最初写的笔记和大家分享下. 我在单位整理的研究笔记: 不同的浏览器对某些CSS代码解析会存在一定的差异,因此就会导致不同浏览器下给用户展 ...
- 第二章 深入 C# 数据类型
第二章 深入 C# 数据类型 1.封装又称信息隐藏,是指利用抽象数据类型将数据和数据的操作结合在一起,使其构成一个不可分割的独立实体,尽可能的隐藏内部的细节,只保留一些对外接口,使之于外部发生联系. ...
- zk系列-zookeeper概述
接触zk是2年前了,最近工作又比较依赖于zk,所以准备起个系列文章,系统的总结下. zookeeper是一个分布式的用于协调的服务,起源于Hadoop中的一个组件.分布式系统可以用zookeeper实 ...