php面向對象之trait


trait的使用技巧
trait是php5.4以后新增加的一個功能,可以將多個類中,共用的一些屬性和方法提取出來做來公共trait類,就像是裝配汽車的配件,如果你的類中要用到這些配件,就直接用use導入就可以了,相當於把trait中的代碼復制到當前類中.
因為trait不是類,所以不能有靜態成員,類常量,當然也不可能被實例化。

其實一個類中的代碼,可以分為二大部分:一是我們自己寫的代碼,暫且叫私有代碼吧,還有一部分就是公共代碼了,之前主要是由父類代碼組成。現在你的類中的公共代碼又多一個新成員:trait類代碼。
如果說:繼承可以縱向擴展一個類,那么trait就是橫向擴展一個類功能

下面以實例進行演示:
//1創建一個trait類Test1

復制代碼
<?php
trait Test1
{
public $name = 'PHP中文網'; //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()
復制代碼

 

trait可以互相嵌套,一個trait類中可以用use導入另一個trait類,理解成代碼復制就可以了.
例如本例中,在Test2中要用到Test1中的代碼,我們只要改動二個地方就可以了。
一是在Test2中用use Test1;導入Test1中的代碼,
二是在Demo1類中的,去掉對Test1的引用,只保留對Test2的引用,想想這是為什么?給大家當作一個思考題吧~
修改后的代碼如下:
//1創建一個trait類Test1

復制代碼
<?php
trait Test1
{
public $name = 'PHP中文網'; //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 Test1, Test2;
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()
復制代碼

 

剛才說過,類中導入的公共代碼,除了trait方法集,還可以有父類,如果在子類中訪問父類中的成員,大家應該很熟悉了,現在一個類除了可以從父類繼承成員,還可以從trait類中繼承,那么有一個問題就不可避免了,如果父類和trait類中的成員命名沖突怎么辦?說人話,就是重名了怎么辦?下面我們以方法重名來演示一下處理方案。
再創建一個類Demo,做為Demo1類的父類。
//3.創建父類Demo

復制代碼
class Demo
{
//在父類中創建一個與Test2重名的方法hello2()
public function hello2()
{
return '父類Demo::hello2()';
}
}
復制代碼

 

代碼如下:

復制代碼
//1創建一個trait類Test1
trait Test1
{
public $name = 'PHP中文網'; //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
{
public function hello2()
{
return '父類Demo::hello2()';
}
}
//4.創建Demo1類
class Demo1 extends Demo
{
// use Test1, Test2;
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()
復制代碼

 

再次訪問,會發現,結果與之前完全一樣沒有任何變化,父類Demo中的hello2方法好像隱身了,壓根不存在一樣的。事實上,父類Demo中的hello2方法當然是存在的,只是被trat類Test2中的同名方法hello2覆蓋掉了,原因就是:trait類中的同名方法,訪問優先級大於父類的同名方法。
如果我們就想訪問父類中的hello2方法,怎么辦呢?只有一個辦法,要么父類方法改名,要么Test2中的方法改名,我們把Test2中的hello2方法改成hello3,再次訪問,就可以看到父類的執行結果了。

那么,我們再進一點想一下,如果在子類也有一個hello2方法呢?那么結果會是什么樣?
我們來試一下,在Demo1類中添加如下代碼:

復制代碼
//4.創建Demo1類
class Demo1 extends Demo
{
// use Test1, Test2;
use Test2;
//在Demo1類中創建與Test2和父類Demo中同名的方法hello2()
public function hello2()
{
return 'Demo1::hello()';
}
}
復制代碼

 


在瀏覽器再次方法,果然不出所料,子類Demo1中的hello2方法的執行結果覆蓋掉了Test2中的同名方法
現在我們總結一下在同一個類中,同名方法的優先級:子類>Trait類>父類,與就是說,誰離調用者越近,誰的優先級就越高。

下面我們再討論最后一個問題:如果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()方法替代Test2中的同名方法
Test1::hello insteadof Test2;
//Test2中的hello()方法用別名訪問
Test2::hello as test2Hello;
} //這里千萬不要加分號 ;
}

//進行測試
$obj = new Demo;
echo $obj->hello(); //訪問Test1中的hello()
echo '<hr>';
echo $obj->test2Hello();//別名訪問Test2中的hello()


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM