如何覆盖特征函数并从被覆盖的函数中调用它?


369

场景:

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

这段代码不起作用,而且我找不到像继承那样来调用trait函数的方法。我打过电话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);
    }
}

没用。

有没有办法使它工作,或者我必须完全重写特质函数,它比这要复杂得多:)

Answers:


640

您的最后一个快到了:

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

2
如果您可以添加有关“无法按预期运行”的更多信息,则@PhillipWhelan会很好。那样写对理解预期的错误行为没有多大帮助,也无法向我们保证这不是您的暂时错误。也许您正在谈论的问题有一些特别的问题?(最终)谢谢。
卡马菲瑟2015年

1
问题在于该特性中的所有其他方法都不再包括在内。
malhal,2015年

2
仅供参考:如果您的trait函数将是静态的,则可以通过调用A::calc(1)
velop

4
正如Phillip提到的(我认为),您将如何对一种特征方法进行此操作,同时仍然包括与正常特征相同的所有其他方法?最好不要明确引用每种方法。
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的特征。

如果需要,特征可以在父类中使用方法(假设您知道该方法在那里),例如

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方法:

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中使用过,则Trait将无用。在这种情况下,最好将calc()方法直接放在该类中。
marcini

这完全对我不起作用。ScreenablePerson::save()存在,Candidate使用Validatingtrait并扩展ScreenablePerson,并且所有三个类都具有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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.