我想知道为什么PHP Trait(PHP 5.4)无法实现接口。
更新自user1460043的回答=> ...不能要求使用它来实现特定接口的类
我理解这可能是显而易见的,因为如果Class A
正在使用Trait T
正在实施interface I
,那么人们会认为Class A
应该是interface I
直接实现Class A
(这不是真的,因为{{1}}可以重命名特征方法。)
就我而言,我的特性是从使用trait的类实现的接口调用方法。
该特征实际上是接口的一些方法的实现。 所以,我想在代码中“设计”每个想要使用我的特性的类都必须实现该接口。这将允许Trait使用接口定义的类方法,并确保它们存在于类中。
答案 0 :(得分:85)
真正的简短版本更简单,因为你不能。这不是Traits的工作方式。
当您在PHP中编写use SomeTrait;
时,您(有效地)告诉编译器将Trait中的代码复制并粘贴到正在使用它的类中。
因为use SomeTrait;
在类中,所以它不能将implements SomeInterface
添加到类中,因为它必须在类之外。
“PHP为什么不是Traits类型?”
因为无法实例化。 Traits实际上只是一个language construct(告诉编译器将特征代码复制并粘贴到这个类中),而不是代码可以引用的对象或类型。
所以,我想在代码中“设计”每个想要使用的类 我的特质必须实现界面。
可以使用抽象类强制执行use
特征,然后从中扩展类。
interface SomeInterface{
public function someInterfaceFunction();
}
trait SomeTrait {
function sayHello(){
echo "Hello my secret is ".static::$secret;
}
}
abstract class AbstractClass implements SomeInterface{
use SomeTrait;
}
class TestClass extends AbstractClass {
static public $secret = 12345;
//function someInterfaceFunction(){
//Trying to instantiate this class without this function uncommented will throw an error
//Fatal error: Class TestClass contains 1 abstract method and must therefore be
//declared abstract or implement the remaining methods (SomeInterface::doSomething)
//}
}
$test = new TestClass();
$test->sayHello();
但是 - 如果您确实需要强制执行任何使用Trait的类具有特定方法,我认为您可能正在使用特征,您应该首先使用抽象类。
或者说你的逻辑是错误的。您的意图是要求实现接口的类具有某些功能,而不是如果它们具有某些功能,则必须将它们声明为实现接口。
修改强>
实际上,您可以在Traits中定义抽象函数以强制类实现该方法。 e.g。
trait LoggerTrait {
public function debug($message, array $context = array()) {
$this->log('debug', $message, $context);
}
abstract public function log($level, $message, array $context = array());
}
然而,这仍然不允许你在特征中实现接口,并且仍然闻起来像一个糟糕的设计,因为接口比定义一个类需要实现的合同的特性要好得多。
答案 1 :(得分:20)
RFC: Traits with interfaces建议将以下内容添加到语言中:
trait SearchItem implements SearchItemInterface
{
...
}
接口所需的方法既可以由trait实现,也可以声明为abstract,在这种情况下,期望使用trait的类实现它。
该语言目前不支持此功能,但正在考虑中(RFC的当前状态为:正在讨论)。
答案 2 :(得分:7)
[...]到"设计"在每个想要使用我的特质的类的代码中 必须实现接口。这将允许Trait使用类方法 由界面定义,并确保它们存在于类中。
这听起来很合理,我不会说你的设计有任何问题。有人提出了这个想法的特点,请参见第二点:
- 特征提供一组实现行为的方法。
- 特征需要一组方法,作为所提供行为的参数。
- [...]
所以说你想要一个需要一个接口的特性可能更合适,而不是"实现"它
我没有看到为什么不应该有这个" trait要求(它的消费者类实现)接口" PHP中的功能,但目前似乎缺少。
正如@Danack在他的answer中所说,你可以在特质中使用抽象函数来要求"要求"他们来自使用特征的类。很遗憾,您无法使用private functions执行此操作。
答案 3 :(得分:0)
我同意@Danack的回复,但是我会补充一点。
真正简短的版本比较简单,因为您做不到。那不是特质的工作方式。
我只能想到在少数情况下您需要的东西是必需的,并且在设计问题上比语言失败更明显,请想象有这样的接口:
interface Weaponize
{
public function hasAmmunition();
public function pullTrigger();
public function fire();
public function recharge();
}
已经创建了一个特征,该特征实现了界面中定义的功能之一,但在此过程中还使用了由界面定义的其他功能>,容易出错,如果使用该功能的类未实现接口,则所有操作均无法触发触发器
trait Triggerable
{
public function pullTrigger()
{
if ($this->hasAmmunition()) {
$this->fire();
}
}
}
class Warrior
{
use Triggerable;
}
一个简单的解决方案就是强制使用 trait 的类也实现这些功能:
trait Triggerable
{
public abstract function hasAmmunition();
public abstract function fire();
public function pullTrigger()
{
if ($this->hasAmmunition()) {
$this->fire();
}
}
}
因此,特征并不完全依赖于界面,而是一项建议,用于实现其功能之一,因为使用特征该类将要求实现抽象方法。
interface Weaponize
{
public function hasAmmunition();
public function pullTrigger();
public function fire();
public function recharge();
}
trait Triggerable
{
public abstract function hasAmmunition();
public abstract function fire();
public function pullTrigger()
{
if ($this->hasAmmunition()) {
$this->fire();
}
}
}
class Warrior implements Weaponize
{
use Triggerable;
public function hasAmmunition()
{
// TODO: Implement hasAmmunition() method.
}
public function fire()
{
// TODO: Implement fire() method.
}
public function recharge()
{
// TODO: Implement recharge() method.
}
}
对不起,英语