Треки
/
PHP
PHP
/
Салабус
/
Коваріантність і контраваріантність
Ко

Коваріантність і контраваріантність у PHP

{one: "1 вправа", few: "%{count} вправи", many: "%{count} вправ", other: "%{count} вправи"}

Про концепцію Коваріантність і контраваріантність

Коваріантність і контраваріантність

Коли ми використовуємо обʼєднані типи, слід памʼятати про поняття коваріантності та контраваріантності. Їх пояснено в документації, але ці приклади можуть допомогти.

Припустімо, що ми маємо клас Calculator, який використовує функцію, як у прикладі вище:

class Calculator
{
    public function add(int|float $x, int|float $y): int|float
    {
        return $x + $y;
    }
}

Якщо ми розширюємо клас, нам дозволено змінювати типи параметрів і типи повернення, але лише за такими правилами.

По-перше, типи параметрів можуть бути «ширшими», тобто приймати більше типів, ніж базовий клас. Наприклад, це дозволено:

class StringCalc extends Foo
{
    public function add(float|int|string $x, float|int|string $y): int|float
    {
        if (!is_numeric($x) || !is_numeric($y)) {
            throw new \InvalidArgumentException('$x and $y must be numeric');
        }
        return $x + $y;
    }
}

Ми могли б підставити StringCalc замість оригінального Calculator, і все ще працювало б. Код, який викликає, усе ще міг би передати float або int, як і раніше, а тип повернення все ще був би очікуваним int або float.

Так само можна звузити тип повернення, тобто зробити його конкретнішим, прибравши типи з обʼєднання. У цьому прикладі використано це правило:

class IntCalc extends Calculator
{
    public function add(float|int $x, float|int $y) : int
    {
        return (int)($x + $y);
    }
}

Оскільки IntCalc усе ще приймає int і float, він і далі може замінити оригінальний Calculator без проблем.

Щоб дізнатися більше про те, що дозволено, а що ні, можна звернутися до принципу підстановки Лісков (LSP).

Щоб побачити приклад того, що було б заборонено, погляньмо на InvalidCalc нижче:

class InvalidCalc extends Calculator
{
    public function add(int $x, int $y) : int|float|string
    {
        // Declaration must be compatible with Calculator->add(x: float|int, y: float|int)

        // Return type declaration must be compatible with Calculator->add(x: float|int, y: float|int) : float|int
    }
}

Клас InvalidCalc не скомпілюється й має дві проблеми. Обмеживши те, що дозволено як параметр, InvalidCalc не міг би замінити оригінальний Calculator, бо більше не приймає float. Крім того, тепер він міг би повертати string, а це також не входить до дозволених типів повернення оригінального класу.

Редагувати через GitHub Посилання відкривається в новому вікні або вкладці