トレイト関数をオーバーライドして、オーバーライドされた関数から呼び出す方法は?


370

シナリオ:

trait A {
    function calc($v) {
        return $v+1;
    }
}

class MyClass {
    use A;

    function calc($v) {
        $v++;
        return A::calc($v);
    }
}

print (new MyClass())->calc(2); // should print 4

このコードは機能せず、継承されたような特性関数を呼び出す方法が見つかりません。私は呼び出してみましたself::calc($v)static::calc($v)parent::calc($v)A::calc($v)および以下:

trait A {
    function calc($v) {
        return $v+1;
    }
}

class MyClass {
    use A {
        calc as traitcalc;
    }

    function calc($v) {
        $v++;
        return traitcalc($v);
    }
}

何も動作しません。

それを機能させる方法はありますか、これよりはるかに複雑な特性関数を完全にオーバーライドする必要があります:)

回答:


641

あなたの最後のものはほとんどそこにありました:

trait A {
    function calc($v) {
        return $v+1;
    }
}

class MyClass {
    use A {
        calc as protected traitcalc;
    }

    function calc($v) {
        $v++;
        return $this->traitcalc($v);
    }
}

特性はクラスではありません。メンバーに直接アクセスすることはできません。それは基本的に自動化されたコピーと貼り付けです...


20
明確にするために-クラスが同じメソッドを定義すると、それは自動的に特性をオーバーライドします。@ircmaxellが空のときに言及するように、トレイトはメソッドを埋めます。
Yehosef 2014年

2
@PhillipWhelanは、「期待どおりに機能しない」ものについてさらに情報を追加できると便利です。そのように書かれていて、それがどのような間違った振る舞いを期待するのか理解するのにあまり役に立たず、これがあなたの一時的な間違いではないことを保証しません。たぶんあなたが話している問題についていくつかのSOの質問がありますか?(最終的に)ありがとう。
カマフェザー2015年

1
問題は、トレイトの他のすべてのメソッドが含まれていないことです。
マルハル2015年

2
参考までに:特性関数が静的である場合は、A::calc(1)
次の

4
Phillipが述べたように(私は思う)、ある特性の1つのメソッドに対してこれをどのように行いながら、同じ特性を持つ他のすべてのメソッドを通常どおりに含めますか?各メソッドを明示的に参照しないことが望ましい。
Gannet

14

クラスがメソッドを直接実装する場合、特性バージョンは使用しません。おそらくあなたが考えているのは:

trait A {
    function calc($v) {
        return $v+1;
    }
}

class MyClass {
    function calc($v) {
        return $v+2;
    }
}

class MyChildClass extends MyClass{
}

class MyTraitChildClass extends MyClass{
    use A;
}

print (new MyChildClass())->calc(2); // will print 4

print (new MyTraitChildClass())->calc(2); // will print 3

子クラスはメソッドを直接実装しないため、親クラスのメソッドを使用する場合は、最初に特性のメソッドを使用します。

必要に応じて、トレイトは親クラスのメソッドを使用できます(メソッドがそこにあることがわかっている場合)。

trait A {
    function calc($v) {
        return parent::calc($v*3);
    }
}
// .... other code from above
print (new MyTraitChildClass())->calc(2); // will print 8 (2*3 + 2)

オーバーライドする方法を提供することもできますが、次のように特性メソッドにアクセスします。

trait A {
    function trait_calc($v) {
        return $v*3;
    }
}

class MyClass {
    function calc($v) {
        return $v+2;
    }
}


class MyTraitChildClass extends MyClass{
    use A {
      A::trait_calc as calc;
    }
}


class MySecondTraitChildClass extends MyClass{
    use A {
      A::trait_calc as calc;
    }

    public function calc($v) {
      return $this->trait_calc($v)+.5;
    }
}


print (new MyTraitChildClass())->calc(2); // will print 6
echo "\n";
print (new MySecondTraitChildClass())->calc(2); // will print 6.5

あなたはそれがhttp://sandbox.onlinephpfunctions.com/code/e53f6e8f9834aea5e038aec4766ac7e1c19cc2b5で動作するのを見ることができます


8

興味がある場合の代替アプローチ-通常のOOO方法を使用するための追加の中間クラス。これにより、parent :: methodnameの使用が簡単になります

trait A {
    function calc($v) {
        return $v+1;
    }
}

// an intermediate class that just uses the trait
class IntClass {
    use A;
}

// an extended class from IntClass
class MyClass extends IntClass {
    function calc($v) {
        $v++;
        return parent::calc($v);
    }
}

6
このアプローチは、traits を使用することで得られる利点をすべて削ります。複数のクラスで複数の特性を組み合わせる(たとえば、クラスの特性A、B、別のクラスの特性B、C、D、別のクラスの特性A、Cなど)
Ionuț Staicu

3
いいえ、このアプローチを使用しても、特性を持つという利点があります。このトレイトはIntClassで使用できますが、必要に応じて他の多くのクラスでも使用できます。トレイトがIntClassでのみ使用されている場合、そのトレイトは役に立たなくなります。その場合は、そのクラスにcalc()メソッドを直接配置することをお勧めします。
marcini

これは完全に私にはうまくいきません。ScreenablePerson::save()存在し、特性をCandidate使用Validatingして拡張しScreenablePerson、3つのクラスすべてにがありsave()ます。
セオドアR.スミス

1

別の特性を使用する:

trait ATrait {
    function calc($v) {
        return $v+1;
    }
}

class A {
    use ATrait;
}

trait BTrait {
    function calc($v) {
        $v++;
        return parent::calc($v);
    }
}

class B extends A {
    use BTrait;
}

print (new B())->calc(2); // should print 4
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.