【问题标题】:Php type hinting not getting along with interfaces and abstract classes?PHP 类型提示与接口和抽象类不相处?
【发布时间】:2011-06-01 19:23:30
【问题描述】:

我认为在代码示例中看到问题比首先编写问题要容易得多。这是我的php代码:

<?php

interface AnInterface
{
        public function method();
}    

class AClass implements AnInterface
{
        public function method()
        {
                echo __METHOD__;
        }
}    

abstract class AnAbstractClass
{
        abstract public function method( AnInterface $Object );
}

class ConcreteClass extends AnAbstractClass
{
        public function method( AClass $Object )
        {
                $Object->method();
        }
}

$Object1 = new ConcreteClass();
$Object2 = new AClass();

$Object1->method( $Object2 );

以上代码导致如下错误:

致命错误:ConcreteClass::method() 的声明必须与 AnAbstractClass::method() 的声明兼容

问题是 php 似乎没有将 AnAbstractClass::method 和 ConcreteClass::method 的签名识别为兼容的。难道我做错了什么?谢谢!

【问题讨论】:

  • 请,养成发布代码生成的错误消息的习惯。发布代码而不发布输出(或错误)是没有用的。

标签: php interface types abstract-class


【解决方案1】:

php 似乎无法将AnAbstractClass::methodConcreteClass::method 的签名识别为兼容的。

PHP 是正确的,它们兼容。通过只允许将AClass(或其子)的实例传递给ConcreteClass::method,您违反了AnAbstractClass 提供的约定:它的任何子类都必须接受AnInterface 作为其method() 的参数.

如果您的示例有效,并且我有另一个类 BClass 实现 AnInterface,我们会遇到这样的情况:根据 AnAbstractClassmethod() 应该接受 BClass 的实例,而根据 @987654333 @,不应该。

更改ConcreteClass::method 的签名以匹配AnAbstractClass::method 的签名。

【讨论】:

    【解决方案2】:

    这是一个示例,说明为什么不允许这样做:

    <?php
    class BClass implements AnInterface { }
    
    function moo(AnAbstractClass $abstract)
    {
        $b = new BClass();
        $abstract->method($b);
    }
    

    这将是一个有效的代码,但如果你将 ConcreteClass 传递给 moo,它会失败,因为它的方法 ConcreteClass::method 不允许 BClass

    这很复杂,但如果你看一个例子就更容易理解。

    【讨论】:

      【解决方案3】:

      不计算。我们昨天也有同样的讨论:
      Can parameter types be specialized in PHP

      所有派生类必须以相同的方式实现方法签名。

      这是理想情况下在运行时检查的内容。但在 PHP 中,解析器可以。 (作为补偿,PHP 不会在解析时检查私有/受保护的属性访问,而是让它在运行时爆掉。)

      如果您想强制执行更严格的类型,我建议:

       assert( is_a($Object, "AClass") );
      

      【讨论】:

      • 鉴于它是 PHP 5,该断言应为 assert($Object instanceof AClass);
      • 是的,这可能行得通,但实际上它不是应该的方式,我想我首先有一个误解,请参阅@meagar 的答案
      • 从 ArrayObject 继承之类的情况将 ISTM 非常适合进行类型断言或在收到错误类型时抛出。但是您不能更改基本接口(例如 ArrayAccess::offsetSet 总是会为值采用混合参数)。
      猜你喜欢
      • 2017-02-19
      • 2016-01-19
      • 2010-11-08
      • 2016-09-27
      • 2016-03-24
      • 2010-12-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多