【问题标题】:Passing an interface to another interface method [duplicate]将接口传递给另一个接口方法[重复]
【发布时间】:2020-02-13 03:33:49
【问题描述】:

我需要在 OOP 中实现一个事件侦听器模型。现在我有EventInterfaceListenerInterface。但我想将事件传递给监听器,如:

interface ListenerInterface {
  public function listen(EventInterface $event);
}

但即使我传递给监听器的Event 实现了EventInterface,我也不能这样做。那我该怎么办?我想把它抽象化以方便使用。

来自 PhpStorm 的屏幕截图:

【问题讨论】:

  • 你应该可以做到。为什么你认为你不能?
  • 那里也给它接口名称。只要您传入的对象实现了该接口,就可以了。
  • userLoggedInEvent 应该实现ListenerInterface,这就是消息所说的,只要不是逻辑,所以你应该检查你的逻辑
  • 您截图中的代码与界面显示中的代码不匹配。在您显示的代码中,接口仅声明方法listen(),但在您的屏幕截图中它抱怨方法__construct() 的签名。请为这个例子添加适当的代码(实际的接口和实现类)作为代码,而不是截图,所以我可以给你一个正确的答案。
  • @Alexxosipov,问题已结束,但如果你能用正确的代码更新它,那就太好了。此外,非常感谢您对以下答案的反馈。

标签: php oop covariance contravariance php-7.4


【解决方案1】:

您的SendUserNotificationListener遵守ListenerInterface 规定的合同。

错误有助于告诉您这一点。如果您尝试运行此代码,您将收到一个致命错误并且您的脚本将崩溃。

如果你有几个这样的接口:

interface ParentInterface {
    public function foo();
}

interface ChildInterface extends ParentInterface {
    public function bar();
}

interface AnotherChildInterface extends ParentInterface {
    public function baz();
}

interface OriginalHandlerInterface
{
    public function handle(ParentInterface $a): string;
}

任何实现OriginalHandlerInterfce 的类都需要完全遵循

例如

class OriginalImplementor implements OriginalHandlerInterface
{
    public function handle(ParentInterface $a) : string
    {
        return class_name($a);
    }
}

如果你试图让实现在参数上使用协方差,它会失败,因为实现不会遵循接口。例如

class WrongOriginalImplementor implements OriginalHandlerInterface
{
    public function handle(ChildInterface $a) : string
    {
        return class_name($a);
    }
}

这样做的逻辑简单而合理。如果有人在使用您创建的类并声明实现了OriginalHandlerInterface,那么该用户根本不需要查看您的实现,他们只需要查看接口即可。

如果原始接口声明 handle() 需要一个 ParentInterface 对象,这意味着我可以将实现 ParentInterface 的类中的对象传递给它,但实现 ChildInterfaceAnotherChildInterface 的类中的对象也将是有效。

另一方面,如果您的实现能够说出handle(ChildInterface $a),那么用户的期望就会被背叛,因为他们将无法从实现AnotherChildInterface的类中传递handle()对象,尽管根据OriginalHandlerInterface 有效。

另一方面,如果你有这个界面:

interface AnotherHandlerInterface
{ 
    public function handle(ChildInterface $a): string;
}

您的实现可以在handle() 参数上使用逆变。他们可以指定限制较少的参数类型。例如:

class ContravariantImplementor implements AnotherHandlerInterface
{
    public function handle(ParentInterface $a): string {
        return class_name($a);
    }
}

这个实现,尽管没有遵循点,是有效的。同样,如果您考虑一下,同样的逻辑也适用:您的类的消费者可以查看AnotherHandlerInterface 并看到handle() 期望ChildInterface。因此,如果遵循原始合同,他们将永远不会被您的实施绊倒。

您的实现限制较少,并且接受原始接口不接受的对象,但这很好,因为协变和逆变规则允许这样做。

请注意,协变和逆变支持在 PHP 中是相当新的,and has been added only in PHP 7.4

你可以看到上面的代码工作here(包括错误)

【讨论】:

    猜你喜欢
    • 2015-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-26
    • 2020-06-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多