Trait是一种在类中实现代码复制的方法.
记住这句话, 你就掌握了Trait的精髓. Trait有很多特性和规则, 但归根结底都是这句话的应用和体现.
定义Trait
trait TezhengA {
public function smallTalk() {
echo 'a';
}
public function bigTalk() {
echo 'A';
}
}
class SK
{
use TezhengA
}
$kl=new SK
$kl->smallTalk(); //'a'
$kl->bigTalk(); //'A'
上例就是最简单的trait定义和调用, 可以将class SK中的trait直接替换成它所包含的代码, 是没有问题的. 所以这就给开发者一个很简单的方式去复制代码, 只需要一个use语句即可!
Trait和普通类定义有哪些区别呢
主要是以下三点:
首先, Trait不是类, 它只是一段可以被复用的代码.
无法通过 trait 自身来实例化。因为它只是一段代码, 所以实例化没有意义.
可以包含抽象方法, 要求让继承子类去定义抽象方法.
trait Asd
{
abstract function Rew();
}
class BDS
{
use Asd;
function Rew()
{
echo'abs'; // TODO: Implement Rew() method.
}
}
$ls=new bds;
var_dump($ls);
可以看到除了抽象类以外, trait也可以定义抽象方法, 并要求使用的类去定义这些方法.
除了以上3点区别以外, trait并没有什么特殊的地方, 它可以包含静态属性, 静态成员, 私有属性, 支持各种可见度.
在使用trait时可能遇到的问题
两个trait中有同名的方法:
trait TezhengA {
public function smallTalk() {
echo 'a';
}
public function bigTalk() {
echo 'A';
}
}
trait TezhengB {
public function smallTalk() {
echo 'b';
}
public function bigTalk() {
echo 'B';
}
}
class Aliased_Talker {
use TezhengA, TezhengB {
TezhengB::smallTalk insteadof TezhengA; //TezhengB::smallTalk优先于A
TezhengA::bigTalk insteadof TezhengB; //TezhengA::bigTalk优先于B
TezhengB::bigTalk as talk; //起别名;
}
}
$as=new Aliased_Talker;
$as->bigTalk(); //A
$as->smallTalk(); //b
$as->talk(); //B
可以使用insteadof关键词指明替代关系, 或者使用as关键词起个别名. 问题解决.
如果trait中的方法和父类重名怎么办?
trait Counter1{
function inc(){
echo'father';
}
}
class C1 {
use Counter1;
}
class C2 extends C1{
use counter1;
function inc()
{
echo 'child';
}
}
$o = new C2();
$o->inc(); //child, 子类自己的方法优先
子类也会继承父类的trait, 当方法名冲突时, 优先级: 当前类的成员 > trait 的方法 > 被继承的方法
如果trait中使用了final, 会有什么影响?
trait Fas {
final public function hello($s) { print "$s, hello!"; }
}
class Bar {
use Fas;
final public function hello($s) { print "hello, $s!"; }
}
class Cee extends Bar
{
public function hello($s) { print "hello!"; } //报错, final在子类生效
}
可以看到, trait中的final关键词在class Bar并没有影响, 但是子类继承中, 却产生了影响.
trait和类可以定义同名属性么?
trait PropertiesTrait {
public $same = true;
public $different = false;
}
class PropertiesExample {
use PropertiesTrait;
public $same = true; // PHP 7.0.0 后没问题,之前版本是 E_STRICT 提醒
public $different = true; // 致命错误
}
通过上例可以看到, 如果同名属性的值也一样, 则正常, 如果同名属性有不同的值, 则会报错.
Trait使用中的一些技巧
Trait可以进行嵌套, trait 也能够使用 trait, trait中所有方法名字不变, 如果两个trait有同名方法, 一样使用insteadof或as解决冲突
trait Hello {
public function sayHello() {
echo 'Hello ';
}
}
trait World {
public function sayHello() {
echo 'World!';
}
}
trait HelloWorld {
use Hello, World{
World:: sayHello instead of Hello;
} //使用2个traits
}
class MyHelloWorld {
use HelloWorld; //调用helloworld
}
$o = new MyHelloWorld();
$o->sayHello(); //可以直接调用子trait中的方法
同时insteadof也可以用一个方法替代多个同名方法:
class TalkerD {
use AD, BD, CD {
AD::sayHello insteadof BD, CD;
AD::sayWorld insteadof BD, CD;
}
}
关键词as可以指定方法可见度:
use HelloWorld { sayHello as protected; }
特别注意
当trait中含有静态属性时, 要注意静态属性的值, 因为不同类use trait时可能会有不同的值:
trait Beer {
protected static $type = 'Light';
public static function printed(){
echo static::$type.PHP_EOL;
}
public static function setType($type){
static::$type = $type;
}
}
class Ale {
use Beer;
}
Beer::setType("Dark"); //通过trait直接调用方法
class Lager {
use Beer;
}
Beer::setType("Amber"); //通过trait直接调用方法
Beer::printed(); // Prints: Amber
Ale::printed(); // Prints: Light
Lager::printed(); //Dark
通过上例可以看到, trait可以直接调用自己的静态方法, 并修改自己的静态属性, 这样导致继承子类会有不同的属性值.
感谢阅读, 如有不准确或错误请留言指正, 我会及时修正, 感谢!
欢迎喜欢技术的小伙伴儿和我交流, 微信1296386616
总结不易, 请勿私自转载, 否则别怪老大爷不客气!
参考资料:
www.php.net PHP官方文档