LoginSignup
12

More than 5 years have passed since last update.

privateメソッドとfinal

Posted at

久々に嵌ったのでメモ。
まず以下の例を見ていただきたい。

privateメソッドをオーバーライドしようとするとどうなるか?
<?php
class A {
    private function echoClass() {
        echo 'A', PHP_EOL;
    }

    public function p() {
        $this->echoClass();
    }
}

class B extends A {
    private function echoClass() {
        echo 'B', PHP_EOL;
    }
}

$a = new A;
$a->p();
$b = new B;
$b->p();

まず、このコードはエラーを吐かず、実行できてしまう。そして A が2回出力される。
クラスBにてechoClassメソッドをオーバーライドしたように見えるけれど、実際はクラスBでの記述は破棄されているように見える。

では次のようにp()をオーバーライドするとどうなるだろうか。

privateメソッドのオーバーライド?その2
<?php
class A {
    private function echoClass() {
        echo 'A', PHP_EOL;
    }

    public function p() {
        $this->echoClass();
    }
}

class B extends A {
    private function echoClass() {
        echo 'B', PHP_EOL;
    }

    public function p() {
        $this->echoClass();
    }
}

$a = new A;
$a->p();
$b = new B;
$b->p();

こちらも実行できる。そして出力結果は AB となる!

privateメソッドは他のクラスからアクセスできない。アクセスできないというか、見えない。つまり、親クラスと子クラスで、まったく別のメソッドを偶然同じ名前で定義した状態であり、これはオーバーライドではない。

ちなみにJavaもこれと同じように動くらしい。

この挙動のおかげで、privateメソッド名に何が消費されているか気にせず、メソッドを定義することができるとも言える。

ただ、protectedと挙動が違うので分かりにくいようにも思う。
もしこの挙動が嫌なのであれば、明示的にfinalを付けると同名のメソッド定義を拒否することができる。

これはコンパイル時にエラーが発生する
<?php
class A {
    final private function echoClass() {
        echo 'A', PHP_EOL;
    }
}

class B {
    private function echoClass() {
        echo 'B', PHP_EOL;
    }
}

コンパイル時のエラー、つまりインタプリタがスクリプトファイルを読み込んだ時点でエラーを発生させるため、すぐに気づくことができるだろう。

追伸

どうでもいいけどechoClass()なんて定義しなくてもget_class($this)や$this::class (こちらはPHP5.5以降)でクラス名取れます

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12