为什么Traits不能直接实例化?

时间:2016-07-07 05:16:30

标签: php class abstract traits php-5.4

在测试PHP中的特征时,我有点困惑为什么会引入特征。我做了一些小实验。首先,我直接在类

中调用trait方法
<?php

trait HelloWorld {
    public function sayHello() {
        echo 'Hello World!';
    }
}

class TheWorldIsNotEnough {
    use HelloWorld;
    public function sayHellos() {
        $o = new HelloWorld();
        $o->sayHello();
    }
}

$o = new TheWorldIsNotEnough();
$o->sayHellos();

?>

我收到了错误

Fatal error: Cannot instantiate trait HelloWorld in C:\xampp\htdocs\test.php on line 35

但是当我这样做时

<?php

trait HelloWorld {
    public function sayHello() {
        echo 'Hello World!';
    }
}
class MyHelloWorld {
    use HelloWorld;
}
class TheWorldIsNotEnough {
    use HelloWorld;
    public function sayHellos() {
        $o = new MyHelloWorld();
        $o->sayHello();
    }
}

$o = new TheWorldIsNotEnough();
$o->sayHellos();

?>

我能够调用特征方法并将结果显示为“Hello World!”。 那么使用Traits有什么好处呢?它与抽象类有什么不同?请帮助我理解使用方法。感谢。

4 个答案:

答案 0 :(得分:7)

Traits不应该被实例化。它们只是代码部分,您可以通过use在类中重用它们。您可以想象,trait代码会扩展并成为您班级的一部分。甚至令人伤心的是:

  

特质基本上是语言辅助的复制和粘贴。

所以你的例子应该是这样的:

<?php

trait HelloWorld {
    public function sayHello() {
        echo 'Hello World!';
    }
}

class TheWorldIsNotEnough {
    use HelloWorld;

    public function sayHellos() {
        // your trait defines this method, so now you can    
        // think that this method is defined in your class directly
        $this->sayHello(); 
    }
}

$o = new TheWorldIsNotEnough();
$o->sayHellos();

//or simply
$o->sayHello();
?>

答案 1 :(得分:1)

好的,这可能不是这样做的方法,但我想办法如何使用Traits以及为什么在某些情况下,对于我的项目来说它更好。它们是课程的一种延伸。如果您与CakePHP很常见,那些特征会让我想起控制器的模型或组件的行为。只需查阅: - )

一个抽象类略有不同,因为你可以像这样使用它来继承:

abstract class HelloWorld {
    public function sayHello() {
        echo "Hello World!";
    }

    abstract public function doFunnyStuff();
    abstract public function doMoreFunnyStuff();
}

class ConcreteHelloWorld extends HelloWorld {
    public function doFunnyStuff() {
        echo "Funny Hello!";
    }

    public function doMoreFunnyStuff() {
        echo "More Funny Hello!";
    }
}

$o = new ConcreteHelloWorld();
$o->sayHello(); // common property
$o->doFunnyStuff(); // specialy implemented property
$o->doMoreFunnyStuff(); // specialy impelemented property

特质更像是一个类的扩展。我在MVC框架中使用Traits以这种方式扩展带有日志的类:

trait Logger
{
    public function saveLog($kindOf, $messasge, $serverity)
    {
        some_connect_to_DB_pseudo_code();
        $sqlQuery = "INSERT INTO log (kindof, message, serverity)
                        VALUES (".$kindof.", ".$message.", ".$serverity.")";
        mysql_query($sqlQuery); // deprecated :-)
    }
}

class Controller extends AppController
{
    use Logger;

    public function someAction($params)
    {
        $this->saveLog("CALL", __METHOD__." - started some Action with params: ".$params, 0);

        ...
        ...
    }
}

它非常方便,因为我在每个类中使用它,并且我不必再次编写所有这些行,我必须连接到数据库并生成SQL查询。因为我通过整个MVC框架有很多继承,所以我不必将Logger作为一些父类包含在内。只需将“use”-keyword放入任何应该能够将loginfo发送到数据库的类中。

同样的事情对我来说对调试消息起作用我在这里写下这样的东西:

$this->debug("WARNING", $message);

我的Debug Trait正在制作一个格式良好的警告信息:-)希望它有助于理解。

答案 2 :(得分:1)

感谢所有发布答案的人,但我经过大量研究后得到了我真正想要的答案。我的问题是什么使得Traits与现有方法(如抽象类,继承等)的不同之处。它在类中调用时实例化的要点是正确的,但最大的区别是我们可以在类中包含多个特征

use class1, class2; 

如果在两个类中存在相同的方法并且我们想要使用class2中的方法时发生冲突,我们就这样做

use class1, class2 {
  class2::method1 insteadof class1;
}

即使是特征也可以通过这种方式定义多个特征:

trait Class1 {
    use trait1, trait2;
}

与继承不同;如果特征具有静态属性,则使用该特征的每个类都具有这些属性的独立实例。 请检查此链接http://php.net/manual/en/language.oop5.traits.php#107965

traits vs inheritance的另一个不同之处是traits中定义的方法可以访问它们所使用的类的方法和属性,包括私有类。 http://php.net/manual/en/language.oop5.traits.php#109508

与接口实现不同,可以访问所有traits方法而无需再次定义它们。

答案 3 :(得分:0)

AbstractTrait类之间唯一的共同点是,无法自行实例化a Trait/an Abstract

但他们的目的不同。 Trait仅用于以细粒度和一致的方式对功能进行分组。它是通过使开发人员在生成不同类层次结构的几个独立类中reuse sets of methods freely来减少单个继承的一些限制,其中Abstract类只是提供一种模板来继承和强制继承类来实现抽象方法。