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.
      Espero que todos estejam bem.
      Seguinte: Tenho um arquivo xml onde alguns campos estão com : (dois pontos), como o exemplo abaixo:
       
      <item>
      <title>
      d sa dsad sad sadasdas
      </title>
      <link>
      dsadas dsa sad asd as dsada
      </link>
      <pubDate>sadasdasdsa as</pubDate>
      <dc:creator>
      d sad sad sa ad as das
      </dc:creator>
      </item>
       
      Meu código:
       
      $link = "noticias.xml"; 
      $xml = simplexml_load_file($link); 
      foreach($xml -> channel as $ite) {     
           $titulo = $ite -> item->title;
           $urltitulo = $ite -> item->link;
           print $urltitulo = $ite -> item->dc:creator;
      } //fim do foreach
      ?>
       
      Esse campo dc:creator eu não consigo ler. Como faço?
       
      Agradeço quem puder me ajudar.
       
      Abs
       
       
    • Por First
      Olá a todos!
       
      Eu estou criando um sistema do zero mas estou encontnrando algumas dificuldades e não estou sabendo resolver, então vim recorrer ajuda de vocês.
      Aqui está todo o meu código: https://github.com/PauloJagata/aprendizado/
       
      Eu fiz um sistema de rotas mas só mostra o conteúdo da '/' não sei porque, quando eu tento acessar o register nada muda.
      E eu também quero que se não estiver liberado na rota mostra o erro de 404, mas quando eu tento acessar um link inválido, nada acontece.
      Alguém pode me ajudar com isso? E se tiver algumas sugestão para melhoria do código também estou aceitando.
       
       
      Desde já, obrigado.
    • Por landerbadi
      Olá pessoal, boa tarde
       
      Tenho uma tabela chamada "produtos" com os seguintes campos (id, produto) e outra tabela chamada "itens" com os seguintes campos (id, prod_01, prod_02, prod_03, prod_04).
       
      Na tabela produtos eu tenho cadastrado os seguintes produtos: laranja, maçã, uva, goiaba, arroz, feijão, macarrão, etc.
       
      Na tabela itens eu tenho cadastrado os itens da seguinte maneira:
       
      1, laranja, uva, arroz, feijão;
      2, maçã, macarrão, goiaba, uva;
      3, arroz, feijão, maçã, azeite
       
      Meu problema é o seguinte: 
      Eu escolho um produto da tabela "produtos", por exemplo "uva".  Preciso fazer uma consulta na tabela "itens" para ser listado todos os registros que contenham o produto "uva" e que todos os demais produtos estejam cadastrados na tabela "produtos".
       
      No exemplo acima seria listado apenas dois registros, pois o terceiro registro não contém o produto "uva". 
       
      Alguém pode me ajudar? Pois estou quebrando a cabeça a vários dias e não consigo achar uma solução.
    • Por landerbadi
      Boa tarde pessoal. Estou tentado fazer uma consulta no banco de dados porém estou tendo dificuldades. Tenho uma tabela chamada "itens" com os seguintes campos: id, item, plural, ativo. Nela tem cadastrado vários itens e seu respectivo plural. No campo ativo eu coloco a letra "S" para informar que esta palavra está ativa no sistema. Por exemplo: 1, casa, casas, S 2, mesa, mesas, S 3, cama, camas, S 4, moto, motos, S 5, rádio, rádios O quinto registro "radio" não está ativo no sistema pois não tem um "S" no campo ativo. E outra tabela chamada "variações" com os seguintes campos (id, item1, item2, item3) com os seguintes registros: 1, casa, camas, moto 2, mesas, casas, radio 3, rádio, cama, mesa Eu preciso fazer uma busca na tabela variações da seguinte maneira: Eu escolho um registro na tabela "itens", por exemplo "casa". Preciso fazer com que o php me liste todos os registros da tabela "variações" que contenham a palavra "casa". Porém se tiver algum registro com a palavra "casas" também tem que ser listado. Neste caso ele irá encontrar dois registros. Agora eu preciso que o php verifique os demais itens e faça a listagem apenas dos item que estão ativos (que contenham um "S" no campo ativo. Neste caso ele irá encontrar apenas um registro, pois o segundo registro contém a palavra "rádio". E "rádio" não está ativo na tabela itens. Como faço isso?
    • Por First
      Olá a todos!
       
      Quando eu tento fazer o login me mostra esse erro "Could not log you in."; Alguém sabe me ajudar a resolver esse problema no meu código?
      <?php require_once("core/init.php"); if (Input::exists()) { if (Token::check(Input::get("token"))) { $validate = new Validate(); $validation = $validate->check($_POST, array( "username" => array("required" => true), "password" => array("required" => true) )); if ($validation->passed()) { $user = new User(); $remember = (Input::get("remember")) === "on" ? true : false; $login = $user->login(Input::get("username"), Input::get("password"), $remember); if ($login) { Session::flash("home", "Welcome back!"); Redirect::to("index.php"); } else { echo "Could not log you in."; } } else { foreach ($validation->errors() as $error) { echo $error."<BR>"; } } } } ?> <form action="" method="POST"> <div class="field"> <label for="username">Username</label> <input type="text" name="username" id="username"> </div> <div class="field"> <label for="password">Password</label> <input type="password" name="password" id="password"> </div> <div class="field"> <label for="remember"> <input type="checkbox" name="remember" id="remember"> Remember me </label> </div> <input type="hidden" name="token" value="<?php echo Token::generate(); ?>"> <input type="submit" value="Log in"> </form>  
       
      Desde já obrigado.
×

Informação importante

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