trait
trait
引文
在php中,为实现代码复用,有了继承,但是一个类只能继承一个父类,不支持多继承,接口支持多实现,但是接口又不太一样,接口对外负责功能调用声明,不负责实现,由实现了接口的类去实现具体功能逻辑,严格意义上来说,不算代码复用,从php5.4开始,php实现了另外一种代码复用的方法,就是下文即将要说的trait。
trait
trait是为扩展类似php单继承的一种代码复用机制,解除单继承语言的限制,使开发人员能够自由地在不同层次结构中组合复用method。trait本身不能实例化,它依托于class存在。传统继承是上下层面的关系,trait则为水平层面的组合。
优先级
对于相同方法名的方法而言,当前类的方法覆盖trait方法,trait中方法覆盖继承的父类方法
class Base { public function sayHello() { echo 'Hello '; } } trait SayWorld { public function sayHello() { parent::sayHello(); echo 'World!'; } public function sayPeter() { echo "\nhello trait peter\n"; } } class MyHelloWorld extends Base { use SayWorld; public function sayPeter() { echo "\nhello class peter\n"; } } $o = new MyHelloWorld(); $o->sayHello(); $o->sayPeter();
输出
多个trait
在类中,可以声明多个trait,将多个trait组合到一个类中
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();
输出
trait冲突
如果trait之间定义了同名的方法,类中又组合了有同名方法的trait,会出现命名冲突,这个时候可以使用insteadof指明调用冲突方法中的某一个。trait中可以用as操作符为某个方法引入别名,注意引入别名并不会对原方法重命名,别名不能和已包含的trait中方法名重复
trait A { public function smallTalk() { echo 'a'; } public function bigTalk() { echo 'A'; } public function helloPeter() { echo "hello peter\n"; } } trait B { public function smallTalk() { echo 'b'; } public function bigTalk() { echo 'B'; } public function helloAlice() { echo "hello alice\n"; } } class Talker { use A, B { B::smallTalk insteadof A; A::bigTalk insteadof B; } } class TalkerAs { use A, B { B::smallTalk insteadof A; A::bigTalk insteadof B; B::bigTalk as talk; } } class TalkerHello { use A, B { B::smallTalk insteadof A; A::bigTalk insteadof B; // A::helloPeter as helloAlice; 别名不允许和已包含trait中方法重名 } } $talker = new Talker(); $talker->smallTalk(); $talker->bigTalk(); echo "\n"; $talkerAs = new TalkerAs(); $talkerAs->smallTalk(); $talkerAs->bigTalk(); $talkerAs->talk();
输出
trait组合trait
正如class可以使用多个trait,trait也可以使用trait
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();
输出
抽象成员方法
在trait中可以定义抽象方法,类中如果要使用trait,必须要实现trait中的抽象方法
trait Hello { public function sayHelloWorld() { echo 'Hello ' . $this->getWorld() . "\n"; } abstract public function getWorld(); } class MyHelloWorld { private $world; use Hello; public function getWorld() { return $this->world; } public function setWorld($val) { $this->world = $val; } } $o = new MyHelloWorld(); $o->sayHelloWorld(); $o->setWorld('world'); $o->sayHelloWorld();
输出
属性
trait中也可以定义属性,但要注意trait定了某个属性后,使用该trait的类中就不能定义同样名称的属性,否则会产生fatal error。(属性如果是兼容的(同样的访问控制符和默认值),就不会产生fatal error,但在php7之前,会有E_STRICT提醒)
trait PropertiesTrait { public $same = true; public $different = false; } class PropertiesExample { use PropertiesTrait; public $same = true; // PHP 7.0.0 后没问题,之前版本是 E_STRICT 提醒 public $different = true; // 致命错误 }
php5.6
php7.1