PHP基礎(chǔ)之類和對(duì)象12——Traits專題
自 PHP 5.4.0 起,PHP 實(shí)現(xiàn)了代碼復(fù)用的一個(gè)方法,稱為 traits。
Traits 是一種為類似 PHP 的單繼承語言而準(zhǔn)備的代碼復(fù)用機(jī)制。Trait 為了減少單繼承語言的限制,使開發(fā)人員能夠自由地在不同層次結(jié)構(gòu)內(nèi)獨(dú)立的類中復(fù)用方法集。Traits 和類組合的語義是定義了一種方式來減少復(fù)雜性,避免傳統(tǒng)多繼承和混入類(Mixin)相關(guān)的典型問題。
Traits 和一個(gè)類相似,但僅僅旨在用細(xì)粒度和一致的方式來組合功能。Trait 不能通過它自身來實(shí)例化。它為傳統(tǒng)繼承增加了水平特性的組合;也就是說,應(yīng)用類的成員不需要繼承。
Example #1 Trait 示例
trait ezcReflectionReturnInfo{ function getReturnType(){} function getReturnDescription(){}}class ezcReflectionMethod extends ReflectionMethod{ use ezcReflectionReturnInfo;}class ezcReflectionFunction extends ReflectionFunction{ use ezcReflectionReturnInfo;}優(yōu)先級(jí)
從基類繼承的成員被 trait 插入的成員所覆蓋。優(yōu)先順序是來自當(dāng)前類的成員覆蓋了 trait 的方法,而 trait 則覆蓋了被繼承的方法。
Example #2 優(yōu)先順序示例
從基類繼承的成員被插入的 SayWorld Trait 中的 MyHelloWorld 方法所覆蓋。其行為 MyHelloWorld 類中定義的方法一致。優(yōu)先順序是當(dāng)前類中的方法會(huì)覆蓋 trait 方法,而 trait 方法又覆蓋了基類中的方法:
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();
輸出結(jié)果:
Hello World!
Example #3 另一個(gè)優(yōu)先級(jí)順序的例子
trait HelloWorld{ public function sayHello(){echo ’Hello World!’; }}class TheWorldIsNotEnough{ use HelloWorld; public function sayHello(){echo ’Hello Universe!’; }}$o = new TheWorldIsNotEnough;$o -> sayHello();
輸出結(jié)果:
Hello Universe!
多個(gè) trait通過逗號(hào)分隔,在 use 聲明列出多個(gè) trait,可以都插入到一個(gè)類中。
Example #4 多個(gè) 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();
輸出結(jié)果:
Hello World!
沖突的解決如果兩個(gè) trait 都插入了一個(gè)同名的方法,如果沒有明確解決沖突將會(huì)產(chǎn)生一個(gè)致命錯(cuò)誤。
為了解決多個(gè) trait 在同一個(gè)類中的命名沖突,需要使用 insteadof 操作符來明確指定使用沖突方法中的哪一個(gè)。
以上方式僅允許排除掉其它方法,as 操作符可以將其中一個(gè)沖突的方法以另一個(gè)名稱來引入。
Example #5 沖突的解決
在本例中 Talker 使用了 trait A 和 B。由于 A 和 B 有沖突的方法,其定義了使用 trait B 中的 smallTalk 以及 trait A 中的 bigTalk。
Aliased_Talker 使用了 as 操作符來定義了 talk 來作為 B 的 bigTalk 的別名。
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; }}$t = new Talker;$t->smallTalk(); //b$t->bigTalk(); //A$at = new Aliased_Talker;$at->smallTalk(); //b$at->bigTalk(); //A$at->talk(); //B修改方法的訪問控制
使用 as 語法還可以用來調(diào)整方法的訪問控制。
Example #6 修改方法的訪問控制
trait HelloWorld{ public function sayHello(){echo ’Hello World!’; }}//修改sayHello的訪問控制class MyClass1{ use HelloWorld{sayHello as protected; }}//給方法一個(gè)改變了訪問控制的別名//原版sayHello的訪問控制則沒有發(fā)生變化class MyClass2{ use HelloWorld{sayHello as private myPrivateHello;}}從 trait 來組成 trait
正如類能夠使用 trait 一樣,其它 trait 也能夠使用 trait。在 trait 定義時(shí)通過使用一個(gè)或多個(gè) trait,它能夠組合其它 trait 中的部分或全部成員。
Example #7 從 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();
輸出結(jié)果:
Hello World!
Trait 的抽象成員為了對(duì)使用的類施加強(qiáng)制要求,trait 支持抽象方法的使用。
Example #8 表示通過抽象方法來進(jìn)行強(qiáng)制要求
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 的靜態(tài)成員
靜態(tài)變量可以被 trait 的方法引用,但不能被 trait 定義。但是 trait 能夠?yàn)槭褂玫念惗x靜態(tài)方法。
Example #9 靜態(tài)變量
trait Counter{ public function inc(){static $c = 0;$c = $c + 1;echo '{$c}<br>'; }}class C1{ use Counter;}class C2{ use Counter;}$o = new C1();$o->inc(); //echo 1$p = new C2;$p->inc(); //echo 1
Example #10 靜態(tài)方法
trait StaticExample{ public static function doSomething(){return ’Doing something.’; }}class Example{ use StaticExample;}Example::doSomething();
輸出結(jié)果:Doing something.
屬性Trait 同樣可以定義屬性。Example #11 定義屬性
trait PropertiesTrait{ public $x = 1;}class PropertiesExample{ use PropertiesTrait;}$example = new PropertiesExample;$example->x;
如果 trait 定義了一個(gè)屬性,那類將不能定義同樣名稱的屬性,否則會(huì)產(chǎn)生一個(gè)錯(cuò)誤。如果該屬性在類中的定義與在 trait 中的定義兼容(同樣的可見性和初始值)則錯(cuò)誤的級(jí)別是?E_STRICT,否則是一個(gè)致命錯(cuò)誤。
Example #12 沖突
trait PropertiesTrait{ public $sname = true; public $different = false;}class PropertiesExample{ use PropertiesTrait; public $sname = true; //Strict Standards public $different = true; //致命錯(cuò)誤}
相關(guān)文章:
1. 使用Hangfire+.NET 6實(shí)現(xiàn)定時(shí)任務(wù)管理(推薦)2. Xml簡(jiǎn)介_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理3. 如何在jsp界面中插入圖片4. jsp實(shí)現(xiàn)登錄驗(yàn)證的過濾器5. phpstudy apache開啟ssi使用詳解6. JSP之表單提交get和post的區(qū)別詳解及實(shí)例7. jsp文件下載功能實(shí)現(xiàn)代碼8. 詳解瀏覽器的緩存機(jī)制9. vue3+ts+elementPLus實(shí)現(xiàn)v-preview指令10. xml中的空格之完全解說
