6

TL;DR

このようにオーバーライド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);
}

魔法のように機能するようです。

ここでこの作業の例を見つけることができます

2 番目の停留所: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. およびの定義にexchangeArray()nextの定義を追加すると、オブジェクトを追加するすべての関数を使用できますか?append()offsetSet()
4

2 に答える 2

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) { }
}

「のみ」は警告を出力します

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 に答える
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 インターフェイスを独自のクラスに実装し、クラスが特定のタイプのオブジェクトでのみ使用されることを意図していることを明確に示すことができます。それはまだ少しぎこちないでしょう、私は恐れています。

ジェネリックがなければ、実行時の instanceof-style チェックなしで一般化された同種のコンテナを持つ方法はありません。唯一の方法は、ClassAArrayObject、ClassBArrayObject などを使用することです。

于 2012-11-27T11:37:23.200 に答える