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

Covariância e contravariância em PHP

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

Sobre Covariância e contravariância

Covariância e contravariância

Quando usas tipos de união, tens de conhecer os conceitos de covariância e contravariância. Estão explicados na documentação, mas estes exemplos podem ajudar.

Suponhamos 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 estenderes a classe, podes alterar os tipos dos parâmetros e os tipos devolvidos, mas apenas de acordo com as seguintes regras.

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

Podíamos substituir o Calculator original por StringCalc e tudo continuaria a funcionar. Quem chama continuaria a poder enviar o float ou int que já podia enviar, e o tipo devolvido continuaria a ser o int ou float esperado.

Do mesmo modo, podes estreitar o tipo devolvido, ou seja, torná-lo mais específico removendo tipos da união. Este exemplo tira partido dessa regra:

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

Como IntCalc continua a aceitar int e float, pode continuar a substituir um Calculator original sem problemas.

Para saberes mais sobre o que é e o que não é permitido, podes consultar o Princípio de Substituição de Liskov (LSP).

Para veres um exemplo do que não seria permitido, dá uma vista de olhos ao 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 compila e tem dois problemas. Ao reduzir o que é aceite como parâmetro, InvalidCalc não poderia substituir o Calculator original, porque já não aceita float. Além disso, agora poderia devolver uma string, o que também não está dentro dos tipos devolvidos permitidos pela classe original.

Editar via GitHub A ligação abre numa nova janela ou separador