trait

參考php

引文php7

在php中,爲實現代碼複用,有了繼承,可是一個類只能繼承一個父類,不支持多繼承,接口支持多實現,可是接口又不太同樣,接口對外負責功能調用聲明,不負責實現,由實現了接口的類去實現具體功能邏輯,嚴格意義上來講,不算代碼複用,從php5.4開始,php實現了另一種代碼複用的方法,就是下文即將要說的trait。oop

traitthis

trait是爲擴展相似php單繼承的一種代碼複用機制,解除單繼承語言的限制,使開發人員可以自由地在不一樣層次結構中組合複用method。trait自己不能實例化,它依託於class存在。傳統繼承是上下層面的關係,trait則爲水平層面的組合。spa

優先級.net

對於相同方法名的方法而言,當前類的方法覆蓋trait方法,trait中方法覆蓋繼承的父類方法3d

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();

輸出code

 多個traitblog

在類中,能夠聲明多個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

相關文章
相關標籤/搜索