在PHP中使用类扩展特征?

37

为什么在PHP中不允许用类扩展Traits?

例如:

Trait T { }

Class C use T {}
/* or */
Class C extends T {}

这种语法是否存在潜在的冲突?我认为没有。


Class C use T {} 除了略有不同的语法以外,与我们已经拥有的有何不同之处? - Jon
当我想要使用trait来扩展某个类的基类时,可以使用更短的语法。 - Meglio
1
@Meglio:就这么干吧:Class C extends Base {use T} - hakre
哦,我刚刚发现我们可以这样做:类 C {使用 T} - 看起来足够清晰和简洁。 - Meglio
3个回答

45

根据PHP手册:

Traits是一种在单继承语言(如PHP)中用于代码重用的机制。Trait旨在通过使开发人员能够在不同类层次结构中自由地重复使用一组方法,从而减少单继承的某些限制。 Traits和类的组合语义被定义为一种降低复杂性并避免与多重继承和Mixins相关的典型问题的方式。

如果您想扩展一个trait,则它应该成为一个类。 如果您有一组方法在一个类中,您想在其他类中使用它们,但感觉将该类扩展(例如class Animal extends Vehicle)不合适,则在PHP 5.4中,将其作为trait可能很好地解决问题。

更直接回答这个问题,你不能“扩展”一个trait,但你可以创建使用其他traits的traits。根据PHP手册:

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

trait World {
    public function sayWorld() {
        echo 'World!';
    }
}

trait HelloWorld {
    use Hello, World;
}

class MyHelloWorld {
    use HelloWorld;
}
你可以将其视为一种维护逻辑分组和引入一些模块化的方式。
编辑:看到一些评论后,我认为值得注意的是,在基类中使用特质也意味着该特质存在于任何扩展它的类中,并且特质函数优先于基类。将其放在子类中只会使特质函数对父/基类不可用。
Parent > Trait > Child

http://php.net/manual/zh/language.oop5.traits.php


36

您可以在一定程度上扩展特征。进一步解释mrlee的回答,当您 use 一个 trait 时,您可以重命名其方法。

比如说,您有一个由框架定义的“父” trait:

trait FrameworkTrait {
    public function keepMe() {
        // Framework's logic that you want to keep
    }

    public function replaceMe() {
        // Framework's logic that you need to overwrite
    }
}

只需引入父特征,将其replaceMe方法重命名为其他名称,并定义自己的replaceMe方法即可。

trait MyTrait {
    use FrameworkTrait {
        FrameworkTrait::replaceMe as frameworkReplaceMe;
    }

    public function replaceMe() {
        // You can even call the "parent" method if you'd like:
        $this->frameworkReplaceMe();

        // Your logic here
    }
}

然后在你的课堂上:

class MyClass {
    use MyTrait;
}

现在,这个类的对象可以执行这些操作:

$obj = new MyClass();
    
$obj->keepMe();             // calls "parent" trait
$obj->replaceMe();          // calls "child" trait
$obj->frameworkReplaceMe(); // calls "parent" trait

8

只有类可以被继承。特质并不是类,尽管它们可以被类使用或包含,但它们本身不是类。如果您认为一个特质需要被继承,那么它可能应该是一个类,可能是一个抽象类,而不是一个特质。


这里的想法是为了更短的语法,而不是实际扩展特征的方式。 - Meglio
3
@Meglio: "语法没有长度,因此没有更短的语法。" - hakre
4
如果我有一些第三方框架(更具体地说是 Laravel),并且我需要其中的一个 trait,但其中有一个被标记为 protected 的函数需要进行小修复怎么办?我不想改变 Laravel 的代码,所以最好有一种方法来覆盖这个受保护的函数,使其在我的自定义 trait 中生效。更具体地说,问题出在一个硬编码的文本字符串上,我希望能够将其本地化。 - JustAMartin
@JustAMartin 请看我的回答。 - andrewtweber
你部分地说错了。接口也可以被扩展。 - xZero

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接