17
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【PHP】継承とprotectedと参照パターン

Last updated at Posted at 2018-08-30

普通の継承 子→親参照

参照というよりは、子クラスに $parentName が(継承されて)定義されているだけの状態

ex1.php
<?php
class ParentClass1
{
    protected string $parentName = "parent\n";
}

class ChildClass1 extends ParentClass1
{
    public function getParentName(): void
    {
        echo $this->parentName;
    }
}

$child = new ChildClass1;
$child->getParentName();

parent

静的な参照 parentとselfの違い

ex2_1.php
<?php
class ParentClass2
{
    protected static string $parentName = "parent\n";
}

class ChildClass2 extends ParentClass2
{
    public static function getParentName(): void
    {
        // parent::$parentName と self::$parentName は同一の存在

        echo "test1:" . parent::$parentName;
        echo "test2:" . self::$parentName;   // 定義を継承している = 自身のプロパティとして parentName を持っている

        self::$parentName = "child\n";       // 代入
        echo "test3:" . parent::$parentName;
        echo "test4:" . self::$parentName;   // 定義を継承している = 自身のプロパティとして parentName を持っている
    }
}

ChildClass2::getParentName();

test1:parent
test2:parent
test3:child
test4:child

ex2_2.php
<?php
class ParentClass3
{
    protected static string $parentName = "parent\n";
}

class ChildClass3 extends ParentClass3
{
    protected static string $parentName = "child\n"; // ChildClass2 との違い

    public static function getParentName ()
    {
        // 子クラスに同名プロパティを定義したとき、
        // parent::$parentNameとself::$parentNameは別物になる

        echo "test1:" . parent::$parentName;
        echo "test2:" . self::$parentName;   // 自身のプロパティとして parentName を持っている

        self::$parentName = "child2\n";      // 代入
        echo "test3:" . parent::$parentName;
        echo "test4:" . self::$parentName;   // 自身のプロパティとして parentName を持っている
    }
}

ChildClass3::getParentName();

test1:parent
test2:child
test3:parent
test4:child2

普通の継承:親から子のプロパティを参照するということ(前置き)

ex1.phpと同じ
参照というよりは、子クラスに parentFunction() が(継承されて)定義されているだけの状態

ex3_1.php
<?php
class ParentClass4
{
    protected string $name = "parent\n";

    protected function parentFunction(): void
    {
        echo $this->name;
    }
}

class ChildClass4 extends ParentClass4
{
    protected string $name = "child\n";

    public function callParentFunction(): void
    {
        $this->parentFunction();
    }
}

$child = new ChildClass4;
$child->callParentFunction();

child

静的な参照 self が指すものと遅延静的束縛

selfはその持ち主のメソッドやプロパティを、
staticは子の持つメソッドやプロパティを参照可能にする

ex3_2.php
<?php
class ParentClass5
{
    protected static string $name = "parent\n";

    protected static function parentFunction(): void
    {
        echo self::$name;
        echo static::$name; // 遅延静的束縛
    }
}

class ChildClass5 extends ParentClass5
{
    protected static string $name = "child\n";

    public static function callParentFunction(): void
    {
        // parent::parentFunction と self::parentFunction の状況は
        // ex2_1.php, ex2_2.php を参照
        self::parentFunction();
    }
}

ChildClass5::callParentFunction();

parent
child

ex3_3.php
<?php
class ParentClass6
{
    protected static string $name = "parent\n";

    protected static function parentFunction(): void
    {
        echo self::$name;
        echo static::$name; // 遅延静的束縛
    }
}

class ChildClass6 extends ParentClass6
{
    protected static string $name = "child\n";

    protected static function parentFunction(): void // ex3_2.php との違い
    {
        echo self::$name;
        echo static::$name;
    }

    public static function callParentFunction(): void
    {
        // parent::parentFunction と self::parentFunction の状況は
        // ex2_1.php, ex2_2.php を参照
        self::parentFunction();
    }
}

ChildClass6::callParentFunction();

child
child

親が子クラスの protected を利用できる ということ

相互参照できるとは書かれていても、使いどころも少ないのであまり詳細は見かけないかも
# Factory パターンとかで使えなくもなさそうだけど、他にやりようはあるし…

よくある誤解

# PHPだからね

ex4_0.php
<?php
class ParentClass7
{
    public function getChildName(): void
    {
        echo $this->name;
    }
}

class ChildClass7 extends ParentClass7
{
    protected string $name = "child\n";
}

$parent = new ParentClass7;
$parent->getChildName();       // ""

$parent->name = "parent_name";
var_dump($parent);             // 親クラスに public の新たなプロパティが追加された
$parent->getChildName();       // 動くようになるが、子クラスの定義は関係ない

object(ParentClass7)#1 (1) {
    ["name"]=>
    string(11) "parent_name"
}
parent_name

インスタンスで見ると直接的な関係が無くても extends があれば protected が読め、なければ読めない

ex4_1_1.php
<?php
class ParentClass8
{
    public function getChildName(): void
    {
        $child = new ChildClass8; // この ChildClass のインスタンス($child)と自身($parent)に直接的な関係がなさそうでも、
        echo $child->name;        // 定義上親子であれば子の protected は参照できる
    }

    public function callChildFunction(): void
    {
        ChildClass8::childFunction(); // インスタンスじゃなくても。
    }
}

class ChildClass8 extends ParentClass8
{
    protected string $name = "child\n";

    protected static function childFunction(): void
    {
        echo "child_function\n";
    }
}

$parent = new ParentClass8;
$parent->getChildName();
$parent->callChildFunction();

child
child_function

ex4_1_2.php
<?php
class ParentClass9
{
    public function getChildName(ChildClass9 $child): void
    {
        echo $child->name; // 定義上親子であれば子のprotectedは参照できる
    }

    public function callChildFunction(ChildClass9 $child): void
    {
        $child->childFunction();
    }
}

class ChildClass9 extends ParentClass9
{
    protected string $name = "child\n";

    protected function childFunction(): void
    {
        echo "child_function\n";
    }
}

$parent = new ParentClass9;
$child = new ChildClass9;
$parent->getChildName($child); // この ChildClass のインスタンス($child)と自身($parent)に直接的な関係がなさそうでも
$parent->callChildFunction($child);

child
child_function

extends がなければもちろん読めない

ex4_2.php
<?php
class ParentClass10
{
    public static function getChildName(): void
    {
        $child = new ChildClass10;
        echo $child->name;
    }

    public static function callChildFunction(): void
    {
        ChildClass10::childFunction();
    }
}

class ChildClass10 /* extends ParentClass10 */
{
    protected string $name = "child\n";

    protected static function childFunction(): void
    {
        echo "child_function\n";
    }
}

ParentClass10::getChildName();

Error: Cannot access protected property ChildClass10::$name

余談

同じクラスの private は、インスタンスが分かれていても読むことができる

ex5.php
<?php
class Test
{
    private string $name = "";

    public function __construct(string $name = "")
    {
        $this->name = $name;
    }

    public function getName(Test $instance): void
    {
        echo $instance->name;
    }
}

$instanceA = new Test("A");
$instanceB = new Test("B");

$instanceA->getName($instanceB);

B

他の言語でも大体同じなので試してみてね

17
19
0

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
17
19

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?