来自PHP手册http://php.net/manual/en/language.oop5.traits.php状态的示例#2
<?php
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();
?>
这是正确的代码,但是在该上下文中使用parent::
是不安全的。假设我编写了自己的“hello world”类,它不继承任何其他类:
<?php
class MyOwnHelloWorld
{
use SayWorld;
}
?>
在我调用sayHello()
方法之前,这段代码不会产生任何错误。这很糟糕。
另一方面,如果特征需要使用某个方法,我可以将该方法编写为抽象的,这很好,因为它确保了特征在编译时被正确使用。但这不适用于父类:
<?php
trait SayWorld
{
public function sayHelloWorld()
{
$this->sayHello();
echo 'World!';
}
public abstract function sayHello(); // compile-time safety
}
所以我的问题是:有没有办法确保(在编译时,而不是在运行时)使用某个特征的类将具有parent::sayHello()
方法?
发布于 2012-10-22 06:14:19
不,没有。事实上,这个例子非常糟糕,因为引入特征的目的是在不依赖继承的情况下将相同的功能引入到许多类中,并且使用parent
不仅要求类具有父类,而且还需要具有特定的方法。
顺便说一句,parent
调用在编译时不会被检查,你可以定义一个简单的类,这个类在它的方法中不会扩展任何父调用,直到其中一个方法被调用,它才会工作。
发布于 2015-04-28 00:26:29
您可以检查$this是否扩展了特定的类或实现了特定的接口:
interface SayHelloInterface {
public function sayHello();
}
trait SayWorldTrait {
public function sayHello() {
if (!in_array('SayHello', class_parents($this))) {
throw new \LogicException('SayWorldTrait may be used only in classes that extends SayHello.');
}
if (!$this instanceof SayHelloInterface) {
throw new \LogicException('SayWorldTrait may be used only in classes that implements SayHelloInterface.');
}
parent::sayHello();
echo 'World!';
}
}
class SayHello {
public function sayHello() {
echo 'Hello ';
}
}
class First extends SayHello {
use SayWorldTrait;
}
class Second implements SayHelloInterface {
use SayWorldTrait;
}
try {
$test = new First();
$test->sayHello(); // throws logic exception because the First class does not implements SayHelloInterface
} catch(\Exception $e) {
echo $e->getMessage();
}
try {
$test = new Second();
$test->sayHello(); // throws logic exception because the Second class does not extends SayHello
} catch(\Exception $e) {
echo $e->getMessage();
}
发布于 2017-01-25 05:00:47
PHP编译阶段只创建字节码。其他的一切都是在运行时完成的,包括多态决策。因此,编译如下所示的代码:
class A {}
class B extends A {
public function __construct() {
parent::__construct();
}
}
但是当run爆炸的时候
$b = new B;
因此,严格来说,您不能在编译时进行parent
检查。您所能做的最好的事情就是尽可能早地将其推迟到运行时。正如其他答案所示,可以使用instanceof
在您的特征方法中实现这一点。就我个人而言,当我知道特征方法需要特定的契约时,我更喜欢使用类型提示。
所有这些都归结为特征的基本目的:编译时、复制和粘贴。就这样。特德对合同一无所知。对多态性一无所知。它们只是提供了一种重用的机制。当与接口结合使用时,它们非常强大,尽管有些冗长。
事实上,first academic discussion of traits揭示了特征应该是“纯粹的”,因为它们对周围的对象一无所知:
特征本质上是一组纯方法,用作类的构建块,是代码重用的基本单元。在这个模型中,类由一组特征组成,通过指定粘合代码将特征连接在一起并访问必要的状态。
"If Traits Weren't Evil, They'd Be Funny"很好地总结了要点、陷阱和选项。我不会分享作者对特征的尖酸刻薄:我在有意义的时候使用它们,并且总是与interface
一起使用。PHP文档中的示例鼓励了不好的行为,这是不幸的。
https://stackoverflow.com/questions/13002598
复制相似问题