6

TL;博士

我想这样重写:offsetSet($index,$value)但它会产生一个致命错误(“声明必须兼容”)。ArrayObjectoffsetSet($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);
}

似乎像魅力一样工作。

你可以在这里找到这个工作的例子

第二站: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() 的声明兼容

你可以在这里看到这个不工作的版本

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

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

你可以在这里看到这个工作的一个版本

问题

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

2 回答 2

3

API 永远不应该变得更具体。

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

这样做的原因是简单的:

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

$ao = new YourArrayObject(); 

如果append有类型要求,那将出错。不过看起来没什么问题。您已经有效地使 API 更加具体,并且不再能够假定对基类的引用具有预期的 API。

基本上可以归结为,如果 API 更加具体,则该子类不再与其父类兼容。

这种奇怪的差异可以通过以下方式看到f:它允许您将 a 传递给它,但随后将在执行Test时失败。$ao->append(5)如果 aecho 'hello world';在它之上,那将执行。我认为这是不正确的行为。

在像 C++、Java 或 C# 这样的语言中,这就是泛型发挥作用的地方。在 PHP 中,恐怕没有一个很好的解决方案。运行时检查会令人讨厌且容易出错,并且滚动您自己的类将完全消除将 ArrayObject 作为基类的优势。不幸的是,希望将 ArrayObject 作为基类也是这里的问题。它存储混合类型,因此您的子类也必须存储混合类型。

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

如果没有泛型,就没有运行时实例样式检查的通用同构容器的方法。唯一的方法是拥有一个 ClassAArrayObject、ClassBArrayObject 等。

于 2012-11-27T11:37:23.200 回答
3
abstract public void offsetSet ( mixed $offset , mixed $value )

ArrayAccess接口声明,public void append ( mixed $value )但没有对应的接口。显然,在后一种情况下,php 比接口更“宽容”/宽松/无论如何。

例如

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

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

"only" 打印警告

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)
于 2012-11-27T11:42:42.850 回答