覆盖函数不兼容

时间:2012-11-27 11:26:45

标签: php override arrayobject

TL; DR

我想从offsetSet($index,$value)覆盖ArrayObject,如下所示:offsetSet($index, MyClass $value)但会产生致命错误(“声明必须兼容”)。

什么&为什么

我正在尝试创建一个ArrayObject子类,强制所有值都是某个对象。我的计划是通过覆盖所有添加值并为其提供类型提示的函数来执行此操作,因此您无法添加除MyClass

值之外的任何内容

如何

第一站:append($value);
来自SPL:

/**
 * Appends the value
 * @link http://www.php.net/manual/en/arrayobject.append.php
 * @param value mixed <p>
 * The value being appended.
 * </p>
 * @return void 
 */
public function append ($value) {}

我的版本:

/**
 * @param MyClass $value
 */
public function append(Myclass $value){
    parent::append($value);
}

似乎像魅力一样工作。

You can find and example of this working here

第二站:offsetSet($index,$value);

再次,来自SPL:

/**
 * Sets the value at the specified index to newval
 * @link http://www.php.net/manual/en/arrayobject.offsetset.php
 * @param index mixed <p>
 * The index being set.
 * </p>
 * @param newval mixed <p>
 * The new value for the index.
 * </p>
 * @return void 
 */
public function offsetSet ($index, $newval) {}

我的版本:

/**
 * @param mixed $index
 * @param Myclass $newval
 */
public function offsetSet ($index, Myclass $newval){
    parent::offsetSet($index, $newval);
}

然而,这会产生以下致命错误:

  

致命错误:声明   Namespace \ MyArrayObject :: offsetSet()必须是   与ArrayAccess :: offsetSet()

的兼容

You can see a version of this NOT working here

如果我这样定义它,那很好:

public function offsetSet ($index, $newval){
    parent::offsetSet($index, $newval);
}

You can see a version of this working here

问题

  1. 为什么不覆盖offsetSet()使用上述代码,但append()呢?
  2. 如果我在exchangeArray()append()旁边添加offsetSet()的定义,我是否拥有添加对象的所有功能?

2 个答案:

答案 0 :(得分:3)

绝不应该使API更具体。

事实上,我认为这是append(Myclass $value)不是致命错误的错误。我认为offsetSet()上的致命错误是正确的。

原因很简单:

function f(ArrayObject $ao) { 
    $ao->append(5); //Error
} 

$ao = new YourArrayObject(); 

如果append具有类型要求,则会出错。但是没有什么看错了。您已经有效地使API更具体,并且不再能够假定对基类的引用具有预期的API。

基本上归结为如果API更具体,那么该子类不再与它的父类兼容。

f可以看到这种奇怪的差异:它允许您将Test传递给它,但会在执行$ao->append(5)时失败。如果echo 'hello world';高于它,那就会执行。我认为这是不正确的行为。

在像C ++,Java或C#这样的语言中,这就是泛型会发挥作用的地方。在PHP中,我担心没有一个漂亮的解决方案。运行时检查会很糟糕且容易出错,并且滚动自己的类将完全抹杀将ArrayObject作为基类的优点。不幸的是,将ArrayObject作为基类的愿望也是这里的问题。它存储混合类型,因此您的子类也必须存储混合类型。

您可以在自己的类中实现ArrayAccess接口,并清楚地标记该类仅用于某种​​类型的对象。我担心这仍然有点笨拙。

如果没有泛型,就没有办法在没有运行时instanceof-style检查的情况下拥有一个通用的齐量容器。唯一的方法是拥有ClassAArrayObject,ClassBArrayObject等

答案 1 :(得分:3)

abstract public void offsetSet ( mixed $offset , mixed $value )

ArrayAccess接口声明,而public void append ( mixed $value )没有相应的接口。显然,php在后一种情况下比使用接口更宽容&#34; / lax /。

e.g。

<?php
class A {
    public function foo($x) { }
}

class B extends A {
    public function foo(array $x) { }
}

&#34;仅&#34;打印警告

Strict Standards: Declaration of B::foo() should be compatible with A::foo($x)

,而

<?php
interface A {
    public function foo($x);
}

class B implements A {
    public function foo(array $x) { }
}

拯救

Fatal error: Declaration of B::foo() must be compatible with A::foo($x)