php trait详解

大家都知道,php类的继承是单继承(接口可以多继承),无法同时从两个基类中继承属性和方法,于是trait就出现啦,可以将多个类中,共用的一些属性和方法提取出来做来公共trait类。

php中的trait是啥?

看上去既像类又像接口,其实都不是,Trait是一种代码复用技术,为PHP的单继承限制提供了一套灵活的代码复用机制。
Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。

其实一个类中的代码,可以分为二大部分:一是我们自己写的代码,暂且叫私有代码吧,还有一部分就是公共代码了,之前主要是由父类代码组成。现在你的类中的公共代码又多一个新成员:trait类代码。
如果说:继承可以纵向扩展一个类, 那么trait就是横向扩展一个类功能

PHP版本要求:

php5.4开始引入trait,其目的就是在于减少代码的重复,增加代码的复用性。

traits的优势是什么

traits不仅仅是可复用代码的集合,它描述了某个特性的属性和方法的集合。可以非常轻量的,以较小的代价随意组合,降低了耦合,可读性也非常好

trait如何使用:

下面以实例进行演示:

demo1

#1创建一个trait类Test1
trait Test1{
    public $name = 'hello world'; //trait类中可以用属性
    public function hello1(){ //trait类中主要成员是方法
        return 'Test1::hello1()';
    }
}

#2.创建triat类Test2
trait Test2{
    function hello2(){
        return 'Test2::hello2()';
    }
}

#3.创建Demo1类
class Demo1{
    use Test1, Test2;
}

#进行测试
$obj = new Demo1;
echo $obj->hello1();     //访问trait类Test1中的hello1()
echo '<hr>';
echo $obj->name;         //访问ttrait类Test1中的$name属性
echo '<hr>';
echo $obj->hello2();     //访问ttrait类Test1中的hello2()

输出结果:

Test1::hello1()
hello world
Test2::hello2()

demo2

trait可以互相嵌套,一个trait类中可以用use导入另一个trait类,理解成代码复制就可以了.
例如本例中,在Test2中要用到Test1中的代码,我们只要改动二个地方就可以了。
一是在Test2中用use Test1;导入Test1中的代码,
二是在Demo1类中的,去掉对Test1的引用,只保留对Test2的引用。

#创建一个trait类Test1
trait Test1{
    public $name = 'hello world'; //trait类中可以用属性
    public function hello1(){ //trait类中主要成员是方法
        return 'Test1::hello1()';
    }
}
#2.创建triat类Test2
trait Test2{
    use Test1;
    function hello2(){
        #在Test2中访问Test1中的属性name,注意语法与普通类是一样的
        return 'Test2::hello2()'.$this->name;
    }
}
#3.创建Demo1类
class Demo1{
    use Test2;
}
#进行测试
$obj = new Demo1;
echo $obj->hello1(); //访问trait类Test1中的hello1()
echo '<hr>';
echo $obj->name; //访问ttrait类Test1中的$name属性
echo '<hr>';
echo $obj->hello2(); //访问ttrait类Test1中的hello2()

输出结果:

Test1::hello1()
hello world
Test2::hello2()hello world

demo3(优先级)

刚才说过,类中导入的公共代码,除了trait方法集,还可以有父类,如果在子类中访问父类中的成员,大家应该很熟悉了,现在一个类除了可以从父类继承成员,还可以从trait类中继承,那么有一个问题就不可避免了,如果父类和trait类中的成员命名冲突怎么办?说人话,就是重名了怎么办?
下面我们以方法重名来演示一下处理方案。
再创建一个类Demo,做为Demo1类的父类。

#1创建一个trait类Test1
trait Test1{
    public $name = 'hello world'; //trait类中可以用属性
    public function hello1(){ //trait类中主要成员是方法
        return 'Test1::hello1()';
    }
}

#2.创建triat类Test2
trait Test2{
    use Test1;
    function hello2(){
        #在Test2中访问Test1中的属性name,注意语法与普通类是一样的
        return 'Test2::hello2()'.$this->name;
    }
}

#3.创建父类Demo
class Demo{
    //在父类中创建一个与Test2重名的方法hello2()
    public function hello2(){
        return '父类Demo::hello2()';
    }
}

#4.创建Demo1类
class Demo1 extends Demo{
    use Test2;
}

#进行测试
$obj = new Demo1;
echo $obj->hello1(); //访问trait类Test1中的hello1()
echo '<hr>';
echo $obj->name; //访问ttrait类Test1中的$name属性
echo '<hr>';
echo $obj->hello2(); //访问ttrait类Test1中的hello2()

输出结果:

Test1::hello1()
hello world
Test2::hello2()hello world

再次访问,会发现,结果与之前完全一样没有任何变化,父类Demo中的hello2方法好像隐身了,压根不存在一样的。事实上,父类Demo中的hello2方法当然是存在的,只是被trat类Test2中的同名方法hello2覆盖掉了,原因就是:trait类中的同名方法,访问优先级大于父类的同名方法。

如果我们就想访问父类中的hello2方法,怎么办呢?只有一个办法,要么父类方法改名,要么Test2中的方法改名,我们把Test2中的hello2方法改成hello3,再次访问,就可以看到父类的执行结果了。

demo4(优先级)

那么,我们再进一点想一下,如果在子类也有一个hello2方法呢?那么结果会是什么样?
我们来试一下,在Demo1类中添加如下代码:

#4.创建Demo1类
class Demo1 extends Demo{
    use Test2;
    #在Demo1类中创建与Test2和父类Demo中同名的方法hello2()
    public function hello2(){
        return 'Demo1::hello()';
    }
}

输出结果:

Test1::hello1()
hello world
Demo1::hello()

在浏览器再次方法,果然不出所料,子类Demo1中的hello2方法的执行结果覆盖掉了Test2中的同名方法
现在我们总结一下在同一个类中,同名方法的优先级:子类>Trait类>父类,与就是说,谁离调用者越近,谁的优先级就越高。

demo5(冲突)

下面我们再讨论最后一个问题:如果trait类中方法重名了,怎么办?如果是trait类中被所有类共享的方法集,重名的可能性是非常大的。
下面我们修改一下代码,删除一些用不到代码:

#1创建一个trait类Test1
trait Test1{
    public function hello(){
        return 'Test1::hello()';
    }
}

#2.创建triat类Test2
trait Test2{
    function hello(){
        return 'Test2::hello()';
    }
}
#3.创建类Demo
class Demo{
    use Test1, Test2{
        Test1::hello insteadof Test2;    #用Test1中的hello()方法替代Test2中的同名方法
        Test2::hello as test2Hello;        #Test2中的hello()方法用别名访问
    } #这里千万不要加分号 ;
}

#进行测试
$obj = new Demo;
echo $obj->hello(); //访问Test1中的hello()
echo '<hr>';
echo $obj->test2Hello();//别名访问Test2中的hello()

输出结果:

Test1::hello()
Test2::hello()


参考文献:
trait技术详解,这次包你学得会

1000

GS

北京 | php攻城狮

创作 35 粉丝 2

fighting