Covariância e Contravariância
No 7.2.0, a contravariância parcial foi introduzida removendo as restrições de tipo
nos parâmetros em um método filho. A partir do PHP 7.4.0, foi adicionado suporte
a covariância e contravariância completas.
A covariância permite que um método de uma classe filha retorne um tipo mais específico que o tipo de retorno
do método da classe pai. A contravariância permite a um tipo de parâmetro ser menos
específico em um método de classe filha, em relação à classe pai.
Uma declaração de tipo é considerada mais específica nos seguintes casos:
Uma classe de tipo é considerada menos específica se o oposto for verdadeiro.
Covariância
Para ilustrar como uma variância funciona, uma classe pai abstrata simples, Animal
é criada. Animal será estendida por classes filhas,
Cat e Dog.
Observe que não há nenhum método que retorne valores neste exemplo. Serão adicionados
alguns métodos que retornam um novo objeto do tipo de classe Animal,
Cat ou Dog.
Contravariância
Continuando com o exemplo anterior com as classes Animal,
Cat e Dog, duas classes chamadas
Food e AnimalFood serão incluídas, e
um método eat(AnimalFood $food) é adicionado à classe abstrata
Animal.
Para ver o comportamento da contravariância, o método
eat é substituído na classe Dog para permitir
qualquer objeto do tipo Food. A classe Cat permanece inalterada.
O próximo exemplo irá mostrar o comportamento da contravariância.
Variância de propriedade
Por padrão, as propriedades não são covariantes nem contravariantes e, portanto, invariantes.
Ou seja, o tipo delas não pode mudar em nenhuma classe filha.
A razão para isso é que as operações "get" devem ser covariantes
e as operações "set" devem ser contravariantes.
A única maneira de uma propriedade satisfazer ambos os requisitos é ser invariante.
A partir do PHP 8.4.0, com a adição de propriedades abstratas (em uma interface ou classe abstrata) e
propriedades virtuais,
é possível declarar uma propriedade que possui apenas uma operação get ou set.
Como resultado, propriedades abstratas ou propriedades virtuais que exigem apenas uma operação "get" podem ser covariantes.
Da mesma forma, uma propriedade abstrata ou propriedade virtual que requerem apenas uma operação "set" pode ser contravariante.
Uma vez que uma propriedade tenha uma operação get e set, entretanto,
ela não será mais covariante ou contravariante para extensão adicional.
Ou seja, agora é invariante.
Exemplo #1 Variância do tipo de propriedade
<?php
class Animal {}
class Dog extends Animal {}
class Poodle extends Dog {}
interface PetOwner
{
// Apenas uma operação get é necessária, portanto isso pode ser covariante.
public Animal $pet { get; }
}
class DogOwner implements PetOwner
{
// Este pode ser um tipo mais restritivo, já que o lado "get"
// ainda retorna um Animal. Porém, como propriedade nativa,
// os filhos desta classe não podem mais alterar o tipo.
public Dog $pet;
}
class PoodleOwner extends DogOwner
{
// Isso NÃO É PERMITIDO, porque DogOwner::$pet tem operações
// get e set definidas e obrigatórias.
public Poodle $pet;
}
?>