Trilhas
/
PHP
PHP
/
Programa
/
Covariância e contravariância
Co

Covariância e contravariância em PHP

{one: "1 exercício", many: "%{count} de exercícios", other: "%{count} exercícios"}

Sobre Covariância e contravariância

Covariância e contravariância

Quando você usa tipos união, precisa conhecer os conceitos de covariância e contravariância. Eles são explicados na documentação, mas estes exemplos podem ajudar.

Suponha que temos uma classe Calculator que usa uma função como no exemplo acima:

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

Se você estender a classe, pode alterar os tipos dos parâmetros e o tipo de retorno, mas apenas de acordo com as regras a seguir.

Primeiro, os tipos dos parâmetros podem ser "mais amplos", ou seja, podem aceitar mais tipos do que a classe base. Por exemplo, isto é permitido:

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;
    }
}

Poderíamos substituir StringCalc no lugar de um Calculator original e tudo continuaria funcionando. Quem chama ainda conseguiria enviar o float ou o int que já podia originalmente, e o tipo de retorno ainda seria o int ou float esperado.

Da mesma forma, você pode estreitar o tipo de retorno, ou seja, torná-lo mais específico removendo tipos da união. Este exemplo aproveita essa regra:

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

Como o IntCalc ainda aceita int e float, ele continua podendo substituir um Calculator original sem problemas.

Para ler mais sobre o que é e o que não é permitido, consulte o Princípio da Substituição de Liskov (LSP).

Para um exemplo do que não seria permitido, dê uma olhada em InvalidCalc abaixo:

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
    }
}

A classe InvalidCalc não vai compilar e tem dois problemas. Ao reduzir o que é aceito como parâmetro, InvalidCalc não poderia substituir o Calculator original, porque ela não aceita mais float. Além disso, ela agora poderia retornar uma string, que também não está entre os tipos de retorno permitidos da classe original.

Editar via GitHub O link abre em uma nova janela ou aba