0Pricing
Kotlin Academy · Aula

Variância no local da declaração: in e out

Use corretamente parâmetros de tipo covariantes (out) e contravariantes (in).

Variância no local da declaração: in e out é uma aula grátis de Kotlin Academy no CoddyKit. Esta é a aula 2 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Kotlin Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Kotlin Academy inclui 4 aulas no total.

O problema da variância

No Kotlin, List<String> é um subtipo de List<Any> porque List é declarado com out T. Sem anotações de variância, isso não seria possível.

val strings: List<String> = listOf("a", "b")
val anys: List<Any> = strings  // OK because List<out T>
// MutableList<String> is NOT a subtype of MutableList<Any>:
// val m: MutableList<Any> = mutableListOf("x") // compile error
fun main() { println(anys) }

Covariância com out

out T significa que a classe só pode produzir valores T (retorná-los), nunca consumi-los. Isso torna Producer<Dog> um subtipo de Producer<Animal>.

interface Producer<out T> {
    fun produce(): T
}
class DogProducer : Producer<String> {
    override fun produce() = "Woof!"
}
fun sound(p: Producer<Any>) = println(p.produce())
fun main() {
    val dog = DogProducer()
    sound(dog)  // OK: Producer<String> is a subtype of Producer<Any>
}

Contravariância com in

in T significa que a classe só pode consumir valores T (aceitá-los como parâmetros), nunca produzi-los. Isso torna Consumer<Animal> um subtipo de Consumer<Dog>.

interface Consumer<in T> {
    fun consume(item: T)
}
class Printer : Consumer<Any> {
    override fun consume(item: Any) = println(item)
}
fun feedDog(consumer: Consumer<String>) = consumer.consume("Dog")
fun main() {
    val printer = Printer()
    feedDog(printer)  // OK: Consumer<Any> is a subtype of Consumer<String>
}

Restrição de out: nenhuma posição in

Com out T, T não pode aparecer na posição de parâmetro (in). O compilador impõe essa regra.

interface ReadOnly<out T> {
    fun get(): T          // OK: out position
    // fun set(t: T) {}  // Error: T in in-position violates out variance
}
class Box<out T>(private val value: T) : ReadOnly<T> {
    override fun get() = value
}
fun main() {
    val box: ReadOnly<Any> = Box<String>("hello")
    println(box.get())
}

Restrição de in: nenhuma posição out

Com in T, T não pode aparecer na posição de retorno (out).

interface WriteOnly<in T> {
    fun set(t: T)        // OK: in position
    // fun get(): T {}  // Error: T in out-position violates in variance
}
class Sink<in T> : WriteOnly<T> {
    override fun set(t: T) = println("Received: $t")
}
fun main() {
    val sink: WriteOnly<String> = Sink<Any>()
    sink.set("hello")
}

Exemplos da biblioteca padrão do Kotlin

Comparable<in T> é contravariante: um Comparable<Number> pode comparar qualquer subtipo de Number. List<out T> é covariante: uma lista de gatos é uma lista de animais.

fun sortNumbers(list: List<Number>) = list.sortedWith(compareBy { it.toDouble() })
fun main() {
    val ints: List<Int> = listOf(3, 1, 2)
    val sorted = sortNumbers(ints)  // OK: List<Int> is List<Number>
    println(sorted)
}

Invariância: MutableList

MutableList<T> é invariante: pode produzir e consumir T, portanto não há relação de subtipo entre tipos T diferentes.

fun addNumber(list: MutableList<Number>) { list.add(1.5) }
fun main() {
    val ints = mutableListOf<Int>(1, 2)
    // addNumber(ints) // compile error: MutableList<Int> is not MutableList<Number>
    val nums = mutableListOf<Number>(1, 2)
    addNumber(nums)   // OK
    println(nums)
}

Variância no ponto de uso como alternativa

Quando não é possível alterar a classe, use a variância no ponto de uso: out no ponto de chamada para projetar um tipo como covariante.

fun copy(from: MutableList<out Any>, to: MutableList<Any>) {
    for (item in from) to.add(item)
}
fun main() {
    val src = mutableListOf("a", "b", "c")
    val dest = mutableListOf<Any>()
    copy(src, dest)
    println(dest)
}

Prática: padrão de repositório

Use covariância para repositórios somente de leitura e contravariância para destinos somente de escrita, modelando um fluxo de dados claro.

interface Repository<out T> {
    fun findAll(): List<T>
    fun findById(id: Int): T?
}
interface Writer<in T> {
    fun save(entity: T)
}
interface ReadWriteRepo<T> : Repository<T>, Writer<T>
// ReadWriteRepo<User> is neither sub nor super of ReadWriteRepo<Admin>

Guia para decidir a variância

Escolha a variância com base na forma como T é usado: out = produtor (apenas retorna T), in = consumidor (apenas aceita T), invariante = ambos (MutableList, Channel).

// Quick mental model:
// out T: source/producer  — List, Flow, Sequence
// in  T: sink/consumer    — Comparable, Continuation
// invariant: read+write   — MutableList, Channel, MutableStateFlow
fun main() {
    val nums: List<Number> = listOf(1, 2, 3)  // List is out
    println(nums)
}

Classe invariante com função out

Mesmo em uma classe invariante, você pode usar out no ponto de uso para parâmetros de funções específicos.

class Stack<T>(private val items: MutableList<T> = mutableListOf()) {
    fun push(item: T) = items.add(item)
    fun pop(): T? = items.removeLastOrNull()
}
fun printAll(stack: Stack<out Any>) {
    // can only read, not write
    println(stack.pop())
}
fun main() {
    val s = Stack<String>()
    s.push("hello")
    printAll(s)
}

Verificação rápida

O que significa out T em um parâmetro de tipo?

Revisão

out T (covariância) permite subtipagem ao produzir valores. in T (contravariância) permite subtipagem ao consumir valores. Tipos invariantes (que leem e escrevem) não têm relação de subtipo entre T diferentes.

Perguntas Frequentes

A aula “Variância no local da declaração: in e out” é grátis?

Sim — o texto completo de “Variância no local da declaração: in e out” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Kotlin Academy, atualize para CoddyKit PRO. O curso de Kotlin Academy inclui 4 aulas no total.

O que vou aprender em “Variância no local da declaração: in e out”?

Use corretamente parâmetros de tipo covariantes (out) e contravariantes (in). Você pratica Kotlin Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Kotlin Academy?

Nenhuma experiência prévia é necessária. Kotlin Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 2 de 4.

Quanto tempo leva a aula “Variância no local da declaração: in e out”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Kotlin Academy?

Sim. Cada aula de Kotlin Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Funções genéricas e restrições de tipos com where
  2. Variância no local da declaração: in e out
  3. Projeção estrela e quando usar *
  4. Apagamento de tipos e parâmetros de tipo reified
← Voltar para Kotlin Academy