PHP类型提示特征

42

我有一个特征。为了创造力,我们称这个特征为“Trait”:

trait Trait{    
    static function treat($instance){    
        // treat that trait instance with care
    }
}

现在,我有一个使用这个特性的类 User。当尝试使用 User 的实例调用 treat 时,一切正常。但我想要类型提示,仅应将使用 Trait 的类的实例作为参数给出,如下所示:

现在,我也有一个使用这个特性的类 User。当尝试使用 User 的实例调用 treat 时,一切都正常。但我希望进行类型提示,以便只能将使用 Trait 的类的实例作为参数传递,例如:

static function treat(Trait $instance){...}

可悲的是,这导致一个致命错误,提示该函数期望一个Trait实例,但提供了一个User实例。这种类型提示对于继承和实现非常有效,但如何为Trait进行类型提示呢?


DaveRandom 是正确的,你需要使用 use 语句来实现 traits,因为它们不是被实例化的。 - phpisuber01
1
@DaveRandom 我认为这是另一个 PHP 的 wtf,请参见 http://eval.in/5936。如果它不会被视为类,为什么会出现“Fatal error: Cannot redeclare class Hello”的错误? - Baba
谢谢大家。@DaveRandom:抱歉,我在手册中没有看到它。感谢提供链接。 - arik
4个回答

20

正如DaveRandom所说,你不能这样做。而且你也不应该这样做。你可能想要实现一个接口并在其上使用类型提示。然后,你可以使用 trait 来实现该接口。


42
我必须说我不同意这个观点。我认为类型提示特性会非常有用。特性允许声明抽象方法,因此如果允许类型提示,它们将比接口更有用,因为a)它们支持多重继承(像接口一样),并且b)它们支持实现(像抽象类一样,可以使用类型提示)。由于PHP不是编译型语言,类型提示几乎是抽象的最好部分,所以特性没有可能发挥出其应有的用处。 - Rob
11
@Rob,这会在方法别名(use myTrait {myMethod as newMethod;})中产生问题,因为可能会发生冲突。假设我有一个类myClass,其中只有一个方法“myMethod”,而trait myTrait提供了一些我想要添加到myClass的功能。我可以将函数“myMethod”重命名为其他名称(例如我的例子中的newMethod)。现在,B人看到这个代码,认为他可以使用类型提示的其他类来使用myClass。但这是不正确的。如果是接口,则必须保持myMethod的意图相同。 - Veda
5
我必须承认,我之前不知道关于Trait方法别名的事情。但是经过一些测试发现,抽象方法无法被别名化。既然D 代表着依赖于抽象,人B不应该依赖于trait的实现,而是应该依赖于抽象方法,比如接口或抽象类。 - Rob
2
答案是正确的,您不希望使用特征作为类型。特征与类中的“use”关键字一起使用的方式意味着您并未指示有关正在创建的类型的任何内容,您只是在实现它。这基本上将其复制并粘贴到该位置。这非常好,因为它允许您实际上不必复制和粘贴,并且不会创建重复项。通常,为了避免复制和粘贴,使用继承会很诱人,但这经常违反适当继承的原则。 - still_dreaming_1
在实际工作中,我参与了一个大量使用继承和特征的项目后,我必须同意上面的评论。特性是一种在不手动复制粘贴的情况下实现接口的方法,因此它们非常有用。在特性和类型系统之间做出心理区分是重要且有帮助的。 - Ilmari

12

对于未来来到这里的人,我想详细解释一下Veda的答案。

  1. 在PHP中,你不能将traits看作traits处理(讽刺的是),如果说您期望一个具有某些traits的对象,则只能通过接口来实现(本质上是抽象traits的集合)。
  2. 其他语言(如Rust)实际上鼓励使用traits进行类型提示。

总之,您的想法并不疯狂!从常识意义上看,将traits视为它们所指的东西确实很有道理,其他语言也是这样做的。出于某种原因,PHP似乎陷入了接口和traits之间的困境。


3
关于第二点:Rust鼓励使用trait进行类型提示,但与PHP不同的是,它不提供接口; 在Rust中,trait是最接近接口的东西。 - Kamafeather

5
如果您想在特质或类中创建使用该特质的函数,可以使用 self 作为类型提示,这样您的函数将如下所示。
static function treat(self $instance){...}

1

可以尝试以下方法:

    /**
     * @param Trait $instance
     */
    static function treat($instance){
    }


这在Intellij Idea上可以工作,但是你的示例无论如何都没有多大意义,trait不是类或接口,它只是一段代码片段,可以在任何使用它的地方放置,因此不要这样做,使用使用这些traits的父类,例如这可能是你正确的解决方案。
然而,我上面编写的代码片段有效,并且我得到了自动完成。

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