Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Dorian Neto

O que são covariância e contravariância?

Recommended Posts

Fala pessoal, tudo tranquilo?

 

Venho estudando as features do PHP 7.2 e vi muito se falar de covariância e contravariância. Sei que esse conceito não é específico da linguagem PHP e sim de OO, mas escolhi postar aqui pois de certa forma a dúvida está ligada ao suporte da linguagem a esse conceito.

 

Dei uma pesquisada no google e até encontrei links bem relevantes, mas gostaria de abrir uma discussão para tentar fixar melhor o significado.

 

Desde já agradeço!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Olá @Dorian Neto.

 

Covariância é a capacidade de você declarar o retorno de um método para um objeto X e após isso, declarar que uma classe filha possa retornar neste mesmo método uma variante do objeto (Y extends X, por exemplo).

Na prática é isso:

<?php
class X {}// uma classe
class Y extends X {}// uma variante daquela classe

class Foo {
    public function method() : X {}// retorna X
}

class Bar extends Foo {
    public function method() : Y {}// alteramos o retorno do método para retornar Y
}

 

Contravariância é a mesma coisa, mas para os parâmetros, ao invés do retorno. Veja:

<?php
class X {}// uma classe
class Y extends X {}// uma variante daquela classe

class Foo {
    public function method( X $param ) {}// recebe X
}

class Bar extends Foo {
    public function method( Y $param ) {}// alteramos o parametro para retornar Y
}

 

Isso seria lindo demais, cara. Há pouco tempo eu precisei exatamente desse recurso, e posso lhe mostrar para você ver na prática a utilidade disso. Veja esse código: https://forum.imasters.com.br/topic/562468-estrutura-de-um-projeto-de-marketing-multinível-com-php-e-mysql/?tab=comments#comment-2243078

 

No arquivo 2 - Mmn/DataStructures/Binary/Tree.php eu construi uma árvore binária.

O método push() dessa árvore recebe um Binary/Node.

E se eu resolvesse criar uma variação dessa árvore com algum comportamento diferente?

<?php
namespace Mmn\DataStructures\Multiary;

use Mmn\DataStructures\Binary;

class SuperTree extends Binary\Tree
{
    /**
     * Observe que esse Node não é Binary, mas Multiary
     * contrariando a definição de Binary\Tree (pai dessa classe)
     */
    public function push( Node $Node )
    {

    }
}

 

.... mas o fato é que o PHP não suporta esses recursos, nem mesmo na sua versão 7.2, então nenhum dos códigos acima funciona.

 

Mas afinal, pq isso virou assunto? Pq o PHP passou a suportar "meio caminho" para isso (que eu particularmente acho inútil para esse propósito, mas ao menos é mais um recurso na linguagem): um novo tipo chamado object.

 

Com isso você agora é capaz de definir parâmetros ou retornos completamente abstratos (mas que sejam objetos), assim:

<?php
// Código copiado do manual
// http://php.net/manual/pt_BR/migration72.new-features.php#migration72.new-features.object-type

function test(object $obj) : object // aqui está a novidade (parametro e retorno)
{
    return new SplQueue();
}

test(new StdClass());

// Esse funciona apenas no PHP 7.2

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Ah, e quase esquecendo... agora há o suporte para Type Widening. Isso também é fundamental para complementar as informações do post anterior, pois dá espaço para a omissão do tipo de parâmetro. Veja o exemplo do manual:

<?php
interface A // também serve para classes
{
    public function Test(array $input);
}

class B implements A
{
    // tipo foi omitido, alterando a declaração do método na classe filho
    public function Test($input){}
}

 

Mas eu ainda acho que é apenas meio caminho para os recursos que você citou...

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Matheus Tavares O que seria essas "declarações" que você faz dentro dos parâmetros dos métodos? Eu já vi algumas vezes isso, mas não sei o nome pra pesquisar sobre.

Exemplo:

public function Test(array $input);

public function push( Node $Node )

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Matheus Tavares sensacional a sua resposta, muito obrigado!

 

Como fiz algumas pesquisas antes da sua resposta, acabei associando as definições que encontrei com a sua e isso me gerou mais dúvidas :P

 

Variância é a capacidade do método se transformar (tipos da entrada e saída) com o intuito de retornar um novo comportamento, isso eu entendi, mas a minha dúvida ainda é no conceito de covariância e contravariância. De acordo com o que eu pesquisei, meu entendimento final foi esse:

  • Covariância: é quando há uma sobrecarga em um método da subclasse onde o tipo do parâmetro ou retorno é transformado em um tipo igual ao do método da superclasse ou mais específico.
  • Contravariância: é o contrário da covariância, ou seja, quando há uma sobrecarga em um método da subclasse onde o tipo do parâmetro ou retorno é transformado em um tipo igual ao do método da superclasse ou mais genérico.

Exemplo de covariância:

<?php

class X
{
    public function foo(): object // tipo genérico
    {
        // ...
    }
}

class Y extends X
{
    public function foo(): string // tipo específico
    {
        // ...
    }
}

 

Exemplo de contravariância:

<?php

class X
{
    public function foo(array $param) // tipo específico
    {
        // ...
    }
}

class Y extends X
{
    public function foo(object $param) // tipo genérico
    {
        // ...
    }	
}

Isso está correto?

 

Covariância só se aplica a retorno e contravariância só se aplica a parâmetros?

 

Além disso, um método invariante quer dizer que o mesmo não sofreu nenhum tipo de transformação, correto?

 

Depois de refletir mais um pouco, pensei se esse conceito está associado diretamente com sobrecarga, recurso esse que o PHP ainda não tem (até tem, mas estranhamente o conceito é totalmente diferente das demais linguagens.). Se realmente essa reflexão que eu fiz com relação a associação de sobrecarga com variância estiver correta, você acha que é isso que falta para esse conceito ser aplicado de vez no PHP?

 

Mais uma vez, muito obrigado pelas resposta e me desculpe pela enxurrada de novos questionamentos :P

 

@BrunoBit o nome disso é Type Hiting. hoje em dia chamado de type declarations ou declaração de tipo ou indução de tipo. Segue o link na documentação: http://php.net/manual/pt_BR/functions.arguments.php#functions.arguments.type-declaration

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Dorian Neto, em nome da praticidade eu acabei "mandando pelos ares" as definições. Falha minha.

 

4 horas atrás, Dorian Neto disse:

Variância é a capacidade do método se transformar (tipos da entrada e saída) com o intuito de retornar um novo comportamento, isso eu entendi

Perfeito, esse é o primeiro conceito. Só seria interessante dizer ainda que eles precisam se relacionar, ser variantes um do outro.

A questão é que a interpretação de variância é relativa de linguagem para linguagem.

Você deu o seguinte exemplo (vou escrever de forma resumida para encurtar a resposta):

class X
    method() : objeto // abstrato

class Y
    method() : string // específico

No contexto do PHP, string é específico de objeto? Não, mas em outras linguagens (como JS ou Java) sim.

No php, uma string é um tipo primitivo (não é um objeto). No JS uma string é um objeto, permitindo você fazer coisas como:

console.log( "minha string".length ); // output: 12

 

As definições que você explicou sobre covariância e contravariância estão certas (a respeito de genérico -> específico e vice-versa). Não preciso complementar nada aqui, apenas reiterar que os exemplos estão incorretos (ambos, por causa da invariância dos tipos).

A ideia é não quebrar as regras (de tipos) originalmente estabelecidas, mas permitir comportamentos variantes.

 

Veja a RFC (implementada na 7.2) a respeito do object typehint. Tem mais informações práticas sobre o assunto: https://wiki.php.net/rfc/object-typehint

 

4 horas atrás, Dorian Neto disse:

Além disso, um método invariante quer dizer que o mesmo não sofreu nenhum tipo de transformação, correto?

Correto, mas é bom dizer ainda que ele além de não ter sofrido, ele também não pode sofrer mutações.

Existem linguagens que implementam isso nativamente, mas nenhuma realmente popular.

Sabe quando as linguagens não oferecem atributos/métodos privados e os programadores nomeiam esses caras com um underscore no início para indicar esse comportamento? É exatamente isso que pode ser feito nesse caso.

Veja no Java. E uma biblioteca que faz uma implementação concreta e mais poderosa para isso no PHP: https://github.com/php-deal/framework.

 

5 horas atrás, Dorian Neto disse:

Covariância só se aplica a retorno e contravariância só se aplica a parâmetros?

Pra afirmar com "só", "sempre", "nunca", o ideal é ter um embasamento muito sólido te acompanhando, que não é o meu caso nesse tópico. Eu diria que sim, mas posso estar enganado.

 

5 horas atrás, Dorian Neto disse:

Depois de refletir mais um pouco, pensei se esse conceito está associado diretamente com sobrecarga

De fato existe uma relação, mas são coisas diferentes.

Você pode usar a sobrecarga como ferramenta para alcançar esses recursos, mas acho que dizer que estão "diretamente associados" é um pouco demais (mas na minha opinião isso é apenas uma verdade relativa, diferente do que temos como experiência do erro e do acerto em busca do conhecimento).

 

5 horas atrás, Dorian Neto disse:

Se realmente essa reflexão que eu fiz com relação a associação de sobrecarga com variância estiver correta, você acha que é isso que falta para esse conceito ser aplicado de vez no PHP?

Eu gosto muito do conceito de sobrecarga e realmente acho muito tosco aqueles métodos mágicos do PHP (apesar de interessante ter o recurso para alguns casos extremos). Se houvesse sobrecarga de fato no PHP, com certeza seria mais fácil aplicar tudo isso.

 

5 horas atrás, Dorian Neto disse:

e me desculpe pela enxurrada de novos questionamentos

Pode perguntar quantas vezes necessário, amigo. Estamos aqui para compartilhar conhecimento. Se eu não souber, alguém vai saber :)

Compartilhar este post


Link para o post
Compartilhar em outros sites
5 horas atrás, Dorian Neto disse:
  • Covariância: é quando há uma sobrecarga em um método da subclasse onde o tipo do parâmetro ou retorno é transformado em um tipo igual ao do método da superclasse ou mais específico.
  • Contravariância: é o contrário da covariância, ou seja, quando há uma sobrecarga em um método da subclasse onde o tipo do parâmetro ou retorno é transformado em um tipo igual ao do método da superclasse ou mais genérico.

 

 

Tirando que é uma sobreescrita (override) e não uma sobrecarga (overload), isso é verdade para a implementação no PHP. Mas só, e somente só, pelo motivo de covariância ser implementada somente para retorno de método e contravariância ser implementada somente para atributos de métodos no PHP.

 

A definição geral é ainda mais simples:

- Covariância: permitir o uso de um objeto mais específico no lugar de um mais genérico;

Contravariância: permitir o uso de um objeto mais genérico no lugar de um mais específico.

 

No caso de sobreescrita (override), é chamado de:

- Covariância/Contravariância de tipo de retorno de método (Covariant/Contravariant method return type);

- Covariância/Contravariância de tipo de argumento de método (Covariant/Contravariant method argument type).

 

O que o PHP está implementado é o seguinte (de forma bem básica, através do uso do tipo object):

- Covariância de tipo de retorno de método;

- Contravariância de tipo de argumento de método.

 

Por que ele não implementa ambos?

 

A resposta é bem simples, fere o Princípcio de Substituição de Liskov. De qualquer forma, existem poucas linguagens que implementam para ambos. Mas ai já não vem ao caso.

 

Existem outros tipos de implementações além de métodos?

 

Sim, pode verificar em C# com uso de Generics. 

https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance

Compartilhar este post


Link para o post
Compartilhar em outros sites

@Matheus Tavares e @Gabriel Heming vocês foram mais do que sensacionais! Depois de todas essas explicações não me resta mais dúvidas.

 

Obrigado pelas correções, eu realmente me equivoquei em algumas coisas :P

 

Abração!

Compartilhar este post


Link para o post
Compartilhar em outros sites

  • Conteúdo Similar

    • Por ILR master
      Fala galera, tudo bem?
       
      Tenho o seguinte codigo:
       
       class Data {
      public static function ExibirTempoDecorrido($date)
      {
          if(empty($date))
          {
              return "Informe a data";
          }
          $periodos = array("segundo", "minuto", "hora", "dia", "semana", "mês", "ano", "década");
          $duracao = array("60","60","24","7","4.35","12","10");
          $agora = time();
          $unix_data = strtotime($date);
          // check validity of date
          if(empty($unix_data))
          {  
              return "Bad date";
          }
          // is it future date or past date
          if($agora > $unix_data) 
          {  
              $diferenca     = $agora - $unix_data;
              $tempo         = "atrás";
          } 
          else 
          {
              $diferenca     = $unix_data - $agora;
              $tempo         = "agora";
          }
          for($j = 0; $diferenca >= $duracao[$j] && $j < count($duracao)-1; $j++) 
          {
              $diferenca /= $duracao[$j];
          }
          $diferenca = round($diferenca);
          if($diferenca != 1) 
          {
              $periodos[$j].= "s";
          }
          return "$diferenca $periodos[$j] {$tempo}";
      }
      }
       
      Funciona redondinho se o valor retornado for de algumas horas, mas...
      Quando passa de dois meses, ele retorna a palavra mess. Deve ser por conta dessa linha
      if($diferenca != 1) 
          {
              $periodos[$j].= "s";
          }
       
      Quero que modre:
       
      2 meses atrás
      e não
      2 mess atrás.
       
      Espero que tenham entendido.
       
      Valeu
    • Por Carlos Web Soluções Web
      Olá...
      Estou tentando fazer o seguinte !!
      Listando dados em tabela !!
      Gostaria que....se na listagem houver 4 linhas...indepedente de seu número de ID, faça a listagem em ID ser em ordem 1 2 3 4 !!
      Exemplo...se tiver uma listagem de dados que está em ID 1 3 3...faça ficar 1 2 3 !!

       
      echo "<table class='tabela_dados' border='1'> <tr> <td>ID</td> <td>Nome Empresa</td> <td>Responsável</td> <td>Telefone 1</td> <td>Telefone 2</td> <td>E-mail 1</td> <td>E-mail 2</td> <td>Endereço</td> <td>CEP</td> <td>Bairro</td> <td>AÇÃO 1</td> <td>AÇÃO 2</td> </tr> "; $sql = "SELECT ID FROM usuarios_dados WHERE Usuario='$usuario'"; $result = $conn->query($sql); $num_rows = $result->num_rows; $Novo_ID = 1; for ($i = 0; $i < $num_rows; $i++) { $registro = $result -> fetch_row(); $sql2 = "UPDATE usuarios_dados SET ID='$Novo_ID' WHERE ID='$Novo_ID'"; $result2 = $conn->query($sql2); $Novo_ID++; } $sql = "SELECT * FROM usuarios_dados"; $result = $conn->query($sql); if ($result->num_rows > 0) { // output data of each row while($row = $result->fetch_assoc()) { echo "<tr> <td>$row[ID]</td> <td>$row[Nome_Empresa]</td> <td>$row[Responsavel]</td> <td>$row[Telefone_1]</td> <td>$row[Telefone_2]</td> <td>$row[Email_1]</td> <td>$row[Email_2]</td> <td>$row[Endereço]</td> <td>$row[CEP]</td> <td>$row[Bairro]</td> <td> <form method='post' action='Editar_Dados.php'> <input type='hidden' name='usuario' value='$usuario'> <input type='hidden' name='senha' value='$senha'> <input type='hidden' name='ID' value='$row[ID]'> <input type='submit' style='padding: 10px;' value='EDITAR'> </form> </td> <td> <form method='post' action='Deletar_Dados.php'> <input type='hidden' name='usuario' value='$usuario'> <input type='hidden' name='senha' value='$senha'> <input type='hidden' name='ID' value='$row[ID]'> <input type='submit' style='padding: 10px;' value='DELETAR'> </form> </td> </tr> "; } } else { echo "0 results"; } $conn->close();  
    • Por ILR master
      Boa tarde pessoal, tudo bem ?
       
      Eu uso o tinymce para cadastro de textos no meu siite, porém, quero fazer um sistema para que os colunistas possam fazer o próprio post.
      O problema do tinymce, é que ele mantém a formatação do texto copiado, como tamanho de fonts, negritos, etc... Quero que o usuário cole o texto e a própria textarea limpe a formatação para que ele formate como quiser.
       
      A pergunta é:
       
      O tinymce tem uma opção para desabilitar a formatação quando um texto é colocado?
      Tem alguma função via java ou php para retirar a formatação assim que o texto é colado?
      Ou é melhor usar um outro editor?
       
      Agradeço deste já.
    • Por Giovanird
      Olá a todos!
      Tenho uma pagina que possui uma DIV onde coloquei uma pagina PHP.
      Uso a função setInterval para atualizar a pagina inclusa dentro da DIV.
      O problema é que ao acessar o site , a DIV só me mostra a pagina inclusa somente quando completo o primeiro minuto.
      Preciso que a pagina inclusa já inicie carregada
       
      Meu código JavaScript e a DIV com a pagina PHP
       
      <script> function atualiza(){ var url = 'direita.php'; $.get(url, function(dataReturn) { $('#direita').html(dataReturn); }); } setInterval("atualiza()",60000); </script> <div> <span id="direita"></span> </div>  
    • Por ILR master
      Fala pessoal.
       
      Seguinte:
       
      Quero selecionar duas tabelas e mostrar com resultados intercalados. Abaixo segue um código explicando para vcs terem uma ideia.
       
      $consulta = "SELECT A.*, B.* FROM tabela1 A, tabela2 B'";
      $resultado = mysqli_query($conexao, $consulta) or die ("erro");
      while($busca = mysqli_fetch_array($resultado)){
       
      print $busca['cod_evento']; --> traz o código da tabela1 
      print $busca['titulo_evento']; -->  traz o titulo da tabela1
      print $busca['cod_noticia']; --> traz o código da tabela2
      print $busca['titulo_noticia']; --> traz o tituloda tabela2
       
      }
       
      Espero que entendam. Grato
       
×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.