Php/docs/language.oop5.variance

提供:Dev Guides
< Php
移動先:案内検索

共変性と反変性

PHP 7.2.0 で、子クラスのメソッドの引数の型の制限を除く形で、反変性が一部サポートされました。 PHP 7.4.0 以降で、共変性と反変性が完全にサポートされるようになりました。

共変性とは、子クラスのメソッドが、親クラスの返り値よりも、より特定の、狭い型を返すことを許すことです。 一方で、反変性とは、親クラスのものよりも、より抽象的な、広い型を引数に指定することを許すものです。

共変性

共変性がどのように動作するかを示すために、 単純な抽象クラスの親であるAnimal を作ることにします。 このクラスは子クラス CatDog に継承されています。

<?phpabstract class Animal{    protected string $name;    public function __construct(string $name)    {        $this->name = $name;    }    abstract public function speak();}class Dog extends Animal{    public function speak()    {        echo $this->name . " barks";    }}class Cat extends Animal {    public function speak()    {        echo $this->name . " meows";    }}

この例では、どのメソッドも値を返さないことに注意して下さい。 以下ではこれらのクラスを使い、 Animal, Cat または Dog クラスの新しいオブジェクトを返すファクトリをいくつか作ってみることにします。

<?phpinterface AnimalShelter{    public function adopt(string $name): Animal;}class CatShelter implements AnimalShelter{    public function adopt(string $name): Cat // Animal 型を返す代わりに、Cat型を返すことができる    {        return new Cat($name);    }}class DogShelter implements AnimalShelter{    public function adopt(string $name): Dog // Animal 型を返す代わりに、Dog型を返すことができる    {        return new Dog($name);    }}$kitty = (new CatShelter)->adopt("Ricky");$kitty->speak();echo "\n";$doggy = (new DogShelter)->adopt("Mavrick");$doggy->speak();

上の例の出力は以下となります。

Ricky meows
Mavrick barks

反変性

既に示した Animal, Cat および Dog クラスの例を引き続き使い、 FoodAnimalFood クラスを追加し、 Animal 抽象クラスに eat(AnimalFood $food) メソッドを追加してみましょう。

<?phpclass Food {}class AnimalFood extends Food {}abstract class Animal{    protected string $name;    public function __construct(string $name)    {        $this->name = $name;    }    public function eat(AnimalFood $food)    {        echo $this->name . " eats " . get_class($food);    }}

反変性 の振る舞いを見るため、Dog クラスの eat メソッドをオーバーライドし、あらゆる Food 型のオブジェクトを受け入れることにします。 Cat クラスは変更していません。

<?phpclass Dog extends Animal{    public function eat(Food $food) {        echo $this->name . " eats " . get_class($food);    }}

さて、反変性がどのように動くかが以下でわかるでしょう。

<?php$kitty = (new CatShelter)->adopt("Ricky");$catFood = new AnimalFood();$kitty->eat($catFood);echo "\n";$doggy = (new DogShelter)->adopt("Mavrick");$banana = new Food();$doggy->eat($banana);

上の例の出力は以下となります。

Ricky eats AnimalFood
Mavrick eats Food

しかし、$kittyeat メソッドに $banana を渡すとどうなるでしょう?

$kitty->eat($banana);

上の例の出力は以下となります。

Fatal error: Uncaught TypeError: Argument 1 passed to Animal::eat() must be an instance of AnimalFood, instance of Food given