---
title: "Herança em Kotlin Tutorial em Português — Passo a Passo | Kotlin Brasil"
url: "https://kotlin.dev.br/tutoriais/heranca-kotlin/"
markdown_url: "https://kotlin.dev.br/tutoriais/heranca-kotlin.MD"
description: "Domine herança em Kotlin: open classes, override, super, classes abstratas, sealed classes é a classe Any com exemplos práticos passo a passo."
date: "2025-06-12"
author: "Karina Melo"
---

# Herança em Kotlin Tutorial em Português — Passo a Passo | Kotlin Brasil

Domine herança em Kotlin: open classes, override, super, classes abstratas, sealed classes é a classe Any com exemplos práticos passo a passo.


Neste tutorial, vamos aprender tudo sobre herança em Kotlin. Herança é um dos pilares da programação orientada a objetos e permite que uma [classe](/glossario/class/) filha reutilize e estenda o comportamento de uma classe pai. Diferente de Java, onde todas as classes são abertas para herança por padrão, o Kotlin adota uma postura mais segura: classes são finais por padrão. Vamos explorar como usar `open`, `override`, `super`, classes [abstratas](/glossario/abstract/), introdução a [sealed classes](/glossario/sealed-class/) é a classe `Any`.

## A Classe Any: A Raiz de Tudo

Em Kotlin, todas as classes herdam implicitamente de `Any`. Essa classe é a raiz da hierarquia de tipos e fornece três métodos fundamentais: `equals()`, `hashCode()` e `toString()`. Qualquer classe que você crie já possui esses métodos herdados.

```kotlin
class MinhaClasse

fun main() {
    val obj = MinhaClasse()

    // Métodos herdados de Any
    println(obj.toString())    // MinhaClasse@<hash>
    println(obj.hashCode())    // Código hash numérico
    println(obj.equals(obj))   // true

    // 'is' verifica o tipo (como instanceof em Java)
    println(obj is Any)        // true — tudo é Any
}
```

Diferente de Java, onde `Object` é a raiz, `Any` em Kotlin não possui os métodos `wait()`, `notify()` e `clone()`. Essa simplificação reflete a filosofia do Kotlin de manter a API limpa e moderna.

## Open Classes e Herança Básica

Por padrão, todas as classes em Kotlin são `final` — não podem ser herdadas. Para permitir que uma classe sirva de base para outras, você precisa marcá-la explicitamente com o modificador `open`. Da mesma forma, métodos e propriedades que podem ser sobrescritos também devem ser `open`.

```kotlin
open class Animal(val nome: String, val peso: Double) {

    open fun emitirSom(): String {
        return "$nome faz um som."
    }

    open fun descricao(): String {
        return "Animal: $nome, Peso: ${peso}kg"
    }

    // Função SEM open — nao pode ser sobrescrita
    fun identificador(): String = "Animal-${nome.uppercase()}"
}

class Cachorro(nome: String, peso: Double, val raca: String) : Animal(nome, peso) {

    override fun emitirSom(): String {
        return "$nome late: Au au!"
    }

    override fun descricao(): String {
        return "Cachorro: $nome ($raca), Peso: ${peso}kg"
    }
}

class Gato(nome: String, peso: Double) : Animal(nome, peso) {

    override fun emitirSom(): String {
        return "$nome mia: Miau!"
    }
}

fun main() {
    val dog = Cachorro("Rex", 25.0, "Pastor Alemão")
    val cat = Gato("Mimi", 4.5)

    println(dog.emitirSom())    // Rex late: Au au!
    println(dog.descricao())    // Cachorro: Rex (Pastor Alemão), Peso: 25.0kg
    println(dog.identificador()) // Animal-REX

    println(cat.emitirSom())    // Mimi mia: Miau!
    println(cat.descricao())    // Animal: Mimi, Peso: 4.5kg
}
```

A decisão de tornar classes finais por padrão é uma escolha deliberada de design. O livro "Effective Java" de Joshua Bloch recomenda "projetar para herança ou proibi-la", e o Kotlin segue exatamente esse princípio. Você precisa pensar conscientemente antes de abrir uma classe para extensão.

## Usando super para Chamar a Classe Pai

A palavra-chave `super` permite acessar implementações da classe pai dentro de uma classe filha. Isso é útil quando você quer estender o comportamento existente em vez de substituí-lo completamente.

```kotlin
open class Veiculo(val marca: String, val modelo: String, val ano: Int) {

    open fun info(): String {
        return "$marca $modelo ($ano)"
    }

    open fun ligar(): String {
        return "Veículo ligado. Verificações basicas realizadas."
    }
}

class CarroEletrico(
    marca: String,
    modelo: String,
    ano: Int,
    val autonomiaKm: Int
) : Veiculo(marca, modelo, ano) {

    override fun info(): String {
        // Usa a implementacao da classe pai e adiciona informacao
        return "${super.info()} — Elétrico, Autonomia: ${autonomiaKm}km"
    }

    override fun ligar(): String {
        val basico = super.ligar()
        return "$basico\nBateria verificada. Motor elétrico pronto."
    }
}

fun main() {
    val tesla = CarroEletrico("Tesla", "Model 3", 2024, 450)
    println(tesla.info())
    // Tesla Model 3 (2024) — Elétrico, Autonomia: 450km

    println(tesla.ligar())
    // Veículo ligado. Verificações basicas realizadas.
    // Bateria verificada. Motor elétrico pronto.
}
```

O padrão de chamar `super` e depois adicionar comportamento específico é muito comum e é considerado boa prática, pois garante que a lógica da classe pai sempre será executada.

## Classes Abstratas

Classes abstratas não podem ser instanciadas diretamente e podem conter tanto membros abstratos (sem implementação) quanto membros concretos (com implementação). Diferente de classes `open`, classes abstratas já são abertas para herança por natureza.

```kotlin
abstract class FormaPagamento(val titular: String) {

    // Método abstrato: subclasses DEVEM implementar
    abstract fun processar(valor: Double): Boolean
    abstract fun nomeMetodo(): String

    // Método concreto: subclasses herdam automaticamente
    fun recibo(valor: Double): String {
        return "Recibo: R$${"%.2f".format(valor)} via ${nomeMetodo()} - Titular: $titular"
    }
}

class CartaoCredito(titular: String, val bandeira: String) : FormaPagamento(titular) {

    override fun processar(valor: Double): Boolean {
        println("Processando R$${"%.2f".format(valor)} no cartão $bandeira de $titular...")
        return valor <= 5000.0 // Limite simplificado
    }

    override fun nomeMetodo() = "Cartão $bandeira"
}

class Pix(titular: String, val chave: String) : FormaPagamento(titular) {

    override fun processar(valor: Double): Boolean {
        println("Processando Pix de R$${"%.2f".format(valor)} para chave $chave...")
        return true // Pix sempre processa (simplificação)
    }

    override fun nomeMetodo() = "Pix ($chave)"
}

fun main() {
    val pagamentos: List<FormaPagamento> = listOf(
        CartaoCredito("Maria Silva", "Visa"),
        Pix("João Santos", "joao@email.com")
    )

    for (pagamento in pagamentos) {
        val sucesso = pagamento.processar(150.0)
        if (sucesso) {
            println(pagamento.recibo(150.0))
        }
        println()
    }
}
```

Classes abstratas são ideais quando você quer fornecer uma implementação parcial que subclasses devem completar. Elas definem um contrato enquanto oferecem código reutilizável — um equilíbrio entre [interfaces](/glossario/interface/) (totalmente abstratas) e classes concretas.

## Introdução a Sealed Classes

[Sealed classes](/glossario/sealed-class/) restringem a hierarquia de herança a um conjunto finito e conhecido de subclasses. Todas as subclasses diretas devem ser declaradas no mesmo arquivo. Isso permite que o compilador saiba exatamente quais tipos são possíveis, habilitando verificações exaustivas com [when](/glossario/when/).

```kotlin
sealed class Resultado {
    data class Sucesso(val dados: String) : Resultado()
    data class Erro(val mensagem: String, val codigo: Int) : Resultado()
    data object Carregando : Resultado()
}

fun tratarResultado(resultado: Resultado): String {
    // O 'when' é exaustivo: o compilador garante que todos os casos são cobertos
    return when (resultado) {
        is Resultado.Sucesso -> "Dados: ${resultado.dados}"
        is Resultado.Erro -> "Erro ${resultado.codigo}: ${resultado.mensagem}"
        is Resultado.Carregando -> "Aguarde, carregando..."
        // Não precisa de 'else' — todos os tipos estão cobertos
    }
}

fun main() {
    val resultados = listOf(
        Resultado.Carregando,
        Resultado.Sucesso("Lista de usuários carregada"),
        Resultado.Erro("Não encontrado", 404)
    )

    for (r in resultados) {
        println(tratarResultado(r))
    }
}
```

Sealed classes são extremamente úteis para modelar estados de uma aplicação, respostas de API e qualquer cenário onde o conjunto de possibilidades é fechado e conhecido. Elas são amplamente usadas no desenvolvimento Android moderno com Jetpack Compose.

## Sobrescrevendo Properties

Além de métodos, você também pode sobrescrever propriedades da classe pai. A propriedade na classe pai deve ser `open`, e a subclasse usa `override`.

```kotlin
open class Conta(open val limite: Double = 1000.0) {
    open val tipo: String = "Básica"

    fun info() = "Conta $tipo — Limite: R$${"%.2f".format(limite)}"
}

class ContaPremium : Conta() {
    override val limite: Double = 10000.0
    override val tipo: String = "Premium"
}

class ContaEmpresarial(override val limite: Double) : Conta() {
    override val tipo: String = "Empresarial"
}

fun main() {
    val basica = Conta()
    val premium = ContaPremium()
    val empresarial = ContaEmpresarial(50000.0)

    println(basica.info())        // Conta Básica — Limite: R$1000.00
    println(premium.info())       // Conta Premium — Limite: R$10000.00
    println(empresarial.info())   // Conta Empresarial — Limite: R$50000.00
}
```

Uma propriedade `val` na classe pai pode ser sobrescrita por uma `var` na subclasse (ampliando o acesso), mas o contrário não é permitido. Isso segue o princípio de que uma subclasse pode ser mais permissiva, mas não mais restritiva.

## Erros Comuns

**Esquecer de marcar a classe com `open`.** Tentar herdar de uma classe sem o modificador `open` resulta em erro de compilação. Essa é a causa mais frequente de confusão para desenvolvedores vindos de Java.

**Esquecer `override` no método sobrescrito.** Diferente de Java onde `@Override` é opcional, em Kotlin o `override` é obrigatório. Omiti-lo causa erro de compilação.

**Chamar métodos abstratos no construtor.** Chamar um método `open` ou abstrato dentro do `init` block ou construtor da classe pai é perigoso, pois a subclasse ainda não foi completamente inicializada nesse ponto.

**Confundir sealed class com enum.** Sealed classes permitem que cada subclasse tenha propriedades e estados diferentes. Enums são constantes únicas. Use sealed quando os tipos filhos precisam de dados distintos.

## Conclusão e Próximos Passos

Neste tutorial, você aprendeu os fundamentos de herança em Kotlin: desde a classe `Any`, passando por `open` classes, `override`, `super`, classes abstratas, sealed classes e sobrescrita de propriedades. Esses conceitos permitem criar hierarquias de tipos seguras e expressivas.

O próximo passo natural é estudar [interfaces em Kotlin](/tutoriais/interfaces-kotlin/), que complementam a herança permitindo múltiplas implementações. Também recomendamos explorar [delegation](/glossario/delegation/), uma alternativa poderosa à herança que favorece composição. Com domínio de herança e interfaces, você terá uma compreensão completa de OOP em Kotlin e estará pronto para projetar arquiteturas robustas em seus projetos.
