Covariância, Contravariância, Limites Superiores e Inferiores em Scala

Entendendo os Modificadores de Variância em Scala

Em Scala, os modificadores de variância controlam a relação de subtipos entre tipos genéricos. Ao definir um tipo covarianet como Lista[+T], Lista[Filho] é um subtipo de Pai. Para um tipo contravariante Lista[-T], Lista[Filho] é um supertipo de Pai.

Covariância em Scala

Considere o seguinte exemplo básico:

class Veiculo {}
class Carro extends Veiculo {}
class Recipiente[T](elemento: T) {}
val recip1: Recipiente[Carro] = new Recipiente(new Carro)
// recip1 não pode ser atribuído a recip2: Recipiente[Veiculo] sem covariância

Ao adicionar o modificador +, a covariância permite a atribuição:

class Veiculo {}
class Carro extends Veiculo {}
class Recipiente[+T](elemento: T) {}
val recip1: Recipiente[Carro] = new Recipiente(new Carro)
val recip2: Recipiente[Veiculo] = recip1 // Válido devido à covariância

Contravariância em Scala

Para ilustrar a contravariância, ajuste o exemplo anterior:

class Veiculo {}
class Carro extends Veiculo {}
class Processador[-T](item: T) {}
val proc1: Processador[Veiculo] = new Processador(new Veiculo)
val proc2: Processador[Carro] = proc1 // Válido, pois Processador[Veiculo] é subtipo de Processador[Carro]

Aqui, a definição contravariante [-T] inverte a relação de subtipos.

Limites Inferiores em Tipos Covariantes

Quando uma classe covairante contém métodos com parâmetros de tipo, pode ocorrer erros de compilação. Por exemplo:

class Veiculo {}
class Carro extends Veiculo {}
class Consumidor[+T](item: T) {
  def usar(t: T) = {} // Erro: posição contravariante
}

Para resolver isso, use um limite inferior:

class Veiculo {}
class Carro extends Veiculo {}
class Consumidor[+T](item: T) {
  def usar[U >: T](u: U) = { println(u) } // Correto
}

Isso ocorre porque parâmetros de método são posições contravariantes, e o limite inferior >: T permite que o método aceite supertipos de T.

Limites Superiores em Tipos Contravariantes

Em classes contravariantes, métodos que retornam o tipo parametrizado requerem um limite superior:

class Veiculo {}
class Carro extends Veiculo {}
class Processador[-T](item: T) {
  def obter[U <: T](): U = { new U } // Limite superior
}

Retornos de métodos são posições covariantes, portanto, o limite superior <: T é necessário para compatiiblidade.

Exemplo Integrado de Variância

Combine covariância, contravariância e limites em um único exemplo:

class Veiculo {}
class Carro extends Veiculo {}
class Gerenciador[-S, +T]() {
  def executar[U >: T](param: U): T = { new T } // Covariância com limite inferior
  def processar[U <: S](dado: S): U = { new U }  // Contravariância com limite superior
}
class Teste extends App {
  val ger1: Gerenciador[Veiculo, Carro] = new Gerenciador()
  val ger2: Gerenciador[Carro, Veiculo] = ger1 // Válido
  ger2.executar(new Veiculo)
  ger2.processar(new Carro)
}

Limites de Visão e Contexto

Os limites de visão (<%) permitem conversões implícitas. Exemplo:

class Ave { def cantar = {} }
class Brinquedo {}
class Verificador[T <% Ave]() {
  def usar(t: T) = t.cantar
}

Ou em métodos:

class Ave { def cantar = {} }
class Brinquedo {}
class Verificador() {
  def usar[T <% Ave](t: T) = t.cantar
}
class Teste extends App {
  val ver = new Verificador()
  ver.usar(new Brinquedo) // Requer conversão implícita
}

Para compilar, adicione uma conversão implícita:

import scala.language.implicitConversions
class Ave { def cantar = {} }
class Brinquedo {}
class Verificador() {
  def usar[T <% Ave](t: T) = t.cantar
}
class Teste extends App {
  implicit def brinquedoParaAve(t: Brinquedo) = new Ave
  val ver = new Verificador()
  ver.usar(new Brinquedo)
}

Limites de contexto, introduzidos no Scala 2.8, usam tipos parametrizados, como Ordered[A]:

def comparar[A : Ordering](x: A, y: A) = implicitly[Ordering[A]].compare(x, y)
def criarArray[A : ClassManifest](tamanho: Int) = new Array[A](tamanho)

Tags: Scala Sistema de Tipos Covariância Contravariância Limites Superiores

Publicado em 6-19 23:18