Kotlin: Herança e Interfaces do Kotlin Explicadas

Última atualização: 2026-08-26

O modelo de herança do Kotlin é mais seguro que o do Java: classes são final por padrão e métodos não são sobrescrevíveis por padrão — Charlie deve declarar explicitamente open para permitir herança, com o compilador protegendo a segurança do polimorfismo.

1. O que Você Aprenderá


2. A História Real de um Arquiteto

(1) Problema: Sobrescrita Acidental Causando Incidentes em Produção

Bob herdou a classe Order para criar SpecialOrder e acidentalmente sobrescreveu o método calculateTotal — causando 500 pedidos especiais com valores incorretos. A equipe financeira emitiu um alerta urgente.

(2) Guarda em Tempo de Compilação do Kotlin

KOTLIN
// Java: anotação override é opcional - sobrescrita silenciosa possível
class SpecialOrder extends Order {
    double calculateTotal() { ... }  // Sobrescreve silenciosamente!
}

// Kotlin: override é OBRIGATÓRIO - compilador captura erros
open class Order {
    open fun calculateTotal(): Double = ...
}
class SpecialOrder : Order() {
    override fun calculateTotal(): Double = ...  // Deve declarar override
}

Anotação obrigatória override + classes final por padrão = compilador protege a segurança da herança. Bob nunca mais fará sobrescrita acidental.


3. Modelo de Herança Open

(1) Classes Final por Padrão

KOTLIN
// Padrão: classe é final (não pode ser herdada)
class Order(val id: String)  // Não pode ser estendida

// Open: permitir herança explicitamente
open class Order(val id: String) {
    // Métodos também são final por padrão
    fun process() { }  // Não pode ser sobrescrito

    // Método open: permitir sobrescrita explicitamente
    open fun calculateTotal(): Double = 0.0
}

// Abstract: deve ser herdada, não pode ser instanciada
abstract class BaseOrder(val id: String) {
    // Método abstrato: deve ser implementado pela subclasse
    abstract fun calculateTotal(): Double

    // Método concreto em classe abstrata
    fun getId(): String = id
}

(2) Hierarquia de Modificadores de Herança

Modificador Classe Método Instanciável Herdável Sobrescrevível
Padrão (final)
open
abstract ✅ (obrigatório) ✅ (obrigatório)

(3) Comparação de Herança: Java vs Kotlin

Dimensão Java Kotlin
Padrão de classe Herdável final (não herdável)
Padrão de método Sobrescrevível final (não sobrescrevível)
Anotação override @Override (opcional) override (obrigatório)
Herança aberta Nenhuma declaração necessária Declaração explícita open
Filosofia de design "Aberto por padrão" "Fechado por padrão"

4. Interfaces e Implementações Padrão

(1) Definição de Interface

KOTLIN
interface Discountable {
    // Método abstrato
    fun discountRate(): Double

    // Implementação padrão
    fun applyDiscount(total: Double): Double {
        return total * (1.0 - discountRate())
    }
}

interface Trackable {
    val trackingCode: String?  // Propriedade abstrata

    fun track(): String = trackingCode?.let { "Rastreamento: $it" } ?: "Ainda não enviado"
}

// Implementar múltiplas interfaces
class PremiumOrder(
    val id: String,
    val total: Double,
    override val trackingCode: String?
) : Discountable, Trackable {
    override fun discountRate() = 0.15  // 15% de desconto
}

(2) Interface vs Classe Abstrata

Dimensão Interface Classe Abstrata
Construtor Nenhum Tem
Estado Nenhum (apenas propriedades abstratas) Tem (propriedades concretas)
Herança múltipla Suportada (múltiplas implementações) Não suportada (herança única)
Implementação padrão Kotlin suporta Suportado
Caso de uso Contrato de comportamento Estado compartilhado + comportamento

5. Anotação Obrigatória override

(1) Uso Básico

KOTLIN
open class Order(val id: String, var status: String) {
    open fun summary(): String = "Pedido $id: $status"
}

class BulkOrder(id: String, status: String, val minQuantity: Int) : Order(id, status) {
    // DEVE usar a palavra-chave override
    override fun summary(): String = "Atacado $id: $status (mín: $minQuantity)"
}

(2) Impedir Sobrescrita Adicional

KOTLIN
open class Order {
    open fun process() { }
}

class SpecialOrder : Order() {
    // Impedir sobrescrita adicional em subclasses
    final override fun process() { }
}

6. Resolução de Conflitos de Herança Múltipla

(1) Problema da Herança em Diamante

KOTLIN
interface A {
    fun hello() = "A"
}

interface B {
    fun hello() = "B"
}

// Diamante: C herda hello() de ambos A e B
class C : A, B {
    // DEVE sobrescrever para resolver o conflito
    override fun hello(): String {
        // Escolher explicitamente qual implementação do pai
        return super<A>.hello()  // Escolher a implementação de A
    }
}

(2) Estratégias de Resolução de Conflitos

KOTLIN
class D : A, B {
    override fun hello(): String {
        // Estratégia 1: Escolher um pai
        return super<B>.hello()

        // Estratégia 2: Combinar ambos
        // return "${super<A>.hello()} + ${super<B>.hello()}"

        // Estratégia 3: Implementação personalizada
        // return "D"
    }
}

(3) Comparação de Resolução de Conflitos

Estratégia Sintaxe Caso de Uso
Escolher pai A super<A>.hello() Implementação de A é mais apropriada
Escolher pai B super<B>.hello() Implementação de B é mais apropriada
Combinar ambos super<A> + super<B> Precisa de ambos os comportamentos
Implementação personalizada Código personalizado Nenhuma é apropriada

7. Diagrama de Hierarquia de Herança

100%
classDiagram
    class Order {
        +val id: String
        +var status: String
        +open fun summary(): String
        +open fun calculateTotal(): Double
    }
    class BulkOrder {
        +val minQuantity: Int
        +override fun calculateTotal(): Double
    }
    class PriorityOrder {
        +val priority: String
        +override fun summary(): String
    }
    class Discountable {
        <<interface>>
        +fun discountRate(): Double
        +fun applyDiscount(total: Double): Double
    }
    class Trackable {
        <<interface>>
        +val trackingCode: String?
        +fun track(): String
    }
    Order <|-- BulkOrder
    Order <|-- PriorityOrder
    BulkOrder ..|> Discountable
    PriorityOrder ..|> Trackable
    PriorityOrder ..|> Discountable

8. Exemplo Completo: Hierarquia de Herança do OrderProcessor

KOTLIN
// ============================================
// OrderProcessor - Hierarquia de Herança
// Funcionalidade: Order / BulkOrder / PriorityOrder
// ============================================

interface Discountable {
    fun discountRate(): Double
    fun applyDiscount(total: Double): Double = total * (1.0 - discountRate())
}

interface Trackable {
    val trackingCode: String?
    fun track(): String = trackingCode?.let { "Rastreamento: $it" } ?: "Ainda não enviado"
}

open class Order(val id: String, var status: String, val total: Double) {
    init {
        require(total >= 0) { "Total deve ser não negativo" }
    }

    open fun summary(): String = "Pedido $id | \$$total USD | $status"
    open fun calculateFinal(): Double = total
}

class BulkOrder(
    id: String,
    status: String,
    total: Double,
    val minQuantity: Int
) : Order(id, status, total), Discountable {

    override fun discountRate() = 0.10  // 10% de desconto por quantidade

    override fun calculateFinal(): Double = applyDiscount(total)

    override fun summary(): String =
        "${super.summary()} | Atacado (mín: $minQuantity) | Final: \$${calculateFinal()} USD"
}

class PriorityOrder(
    id: String,
    status: String,
    total: Double,
    val priority: String,
    override val trackingCode: String?
) : Order(id, status, total), Discountable, Trackable {

    override fun discountRate() = when (priority) {
        "VIP" -> 0.20
        "GOLD" -> 0.15
        else -> 0.05
    }

    override fun calculateFinal(): Double = applyDiscount(total)

    override fun summary(): String =
        "${super.summary()} | Prioridade: $priority | ${track()} | Final: \$${calculateFinal()} USD"
}

fun main() {
    val orders = listOf(
        Order("ORD-001", "CONFIRMED", 299.99),
        BulkOrder("ORD-002", "CONFIRMED", 5_000.00, 100),
        PriorityOrder("ORD-003", "SHIPPED", 15_000.00, "VIP", "TRK-XYZ789"),
        PriorityOrder("ORD-004", "PENDING", 2_500.00, "GOLD", null)
    )

    println("=== Resumo de Pedidos ===")
    orders.forEach { println(it.summary()) }

    val totalSavings = orders
        .filterIsInstance<Discountable>()
        .sumOf { it.applyDiscount((it as Order).total) - (it as Order).calculateFinal() }

    println("\nEconomia total com descontos: \$$totalSavings USD")
}

Saída:

TEXT 📖 Somente leitura
=== Resumo de Pedidos ===
Pedido ORD-001 | $299.99 USD | CONFIRMED
Pedido ORD-002 | $5000.0 USD | CONFIRMED | Atacado (mín: 100) | Final: $4500.0 USD
Pedido ORD-003 | $15000.0 USD | SHIPPED | Prioridade: VIP | Rastreamento: TRK-XYZ789 | Final: $12000.0 USD
Pedido ORD-004 | $2500.0 USD | PENDING | Prioridade: GOLD | Ainda não enviado | Final: $2125.0 USD

Economia total com descontos: $3475.0 USD

9. Exemplos práticos rápidos

▶ Exemplo: Herança simples

KOTLIN
// Por padrão todas as classes são final - devem ser marcadas open para herança
open class Shape(val name: String) {
    open fun area(): Double = 0.0

    fun describe() = "Forma: $name, área=${area()}"
}

class Circle(name: String, val radius: Double) : Shape(name) {
    override fun area(): Double = Math.PI * radius * radius
}

class Rectangle(name: String, val width: Double, val height: Double) : Shape(name) {
    override fun area(): Double = width * height
}

val shapes = listOf(
    Circle("círculo", 5.0),
    Rectangle("retângulo", 4.0, 6.0)
)

shapes.forEach { println(it.describe()) }

Saída:

TEXT 📖 Somente leitura
Forma: círculo, área=78.53981633974483
Forma: retângulo, área=24.0

▶ Exemplo: Interfaces

KOTLIN
// Interface pode ter propriedades abstratas e implementações padrão
interface Printable {
    fun print()
    fun preview(): String = "Prévia de $this"
}

interface Loggable {
    fun log() = println("[LOG] $this")
}

class Document(val title: String, val content: String) : Printable, Loggable {
    override fun print() {
        println("== $title ==")
        println(content)
    }
    override fun preview() = "Prévia: ${title.substring(0, minOf(title.length, 20))}"
}

val doc = Document("Relatório Anual", "Receita total: $1M")
println(doc.preview())
doc.print()
doc.log()

Saída:

TEXT 📖 Somente leitura
Prévia: Relatório Anual
== Relatório Anual ==
Receita total: $1M
[LOG] Document(title=Relatório Anual, content=Receita total: $1M)

▶ Exemplo: Conflitos em interfaces

KOTLIN
interface A {
    fun greet() = println("Olá de A")
}

interface B {
    fun greet() = println("Olá de B")
}

class C : A, B {
    // Deve resolver conflito explicitamente
    override fun greet() {
        super<A>.greet()
        super<B>.greet()
        println("Olá de C")
    }
}

val c = C()
c.greet()

Saída:

TEXT 📖 Somente leitura
Olá de A
Olá de B
Olá de C

▶ Exemplo: Classes abstratas

KOTLIN
abstract class Vehicle(val maxSpeed: Double) {
    abstract fun move(): String
    abstract fun fuelType(): String

    fun describe() = "${this::class.simpleName}: ${move()}, combustível=${fuelType()}, maxSpeed=${maxSpeed}km/h"
}

class Car(maxSpeed: Double, val fuel: String) : Vehicle(maxSpeed) {
    override fun move() = "Dirigindo"
    override fun fuelType() = fuel
}

class Boat(maxSpeed: Double) : Vehicle(maxSpeed) {
    override fun move() = "Navegando"
    override fun fuelType() = "Diesel"
}

val vehicles = listOf(
    Car(180.0, "Gasolina"),
    Car(220.0, "Elétrico"),
    Boat(60.0)
)

vehicles.forEach { println(it.describe()) }

Saída:

TEXT 📖 Somente leitura
Car: Dirigindo, combustível=Gasolina, maxSpeed=180.0km/h
Car: Dirigindo, combustível=Elétrico, maxSpeed=220.0km/h
Boat: Navegando, combustível=Diesel, maxSpeed=60.0km/h

▶ Exemplo: Polimorfismo e upcasting

KOTLIN
open class Animal(val name: String) {
    open fun speak() = "$name faz som"
}

class Dog(name: String) : Animal(name) {
    override fun speak() = "$name: Au au!"
}

class Cat(name: String) : Animal(name) {
    override fun speak() = "$name: Miau!"
}

// Upcasting: Dog/Cat tratados como Animal
val animals: List<Animal> = listOf(
    Dog("Rex"),
    Cat("Whiskers"),
    Dog("Buddy")
)

animals.forEach { println(it.speak()) }

// Usando `is` para downcast com segurança
animals.forEach { animal ->
    when (animal) {
        is Dog -> println("${animal.name} é um cachorro")
        is Cat -> println("${animal.name} é um gato")
    }
}

Saída:

TEXT 📖 Somente leitura
Rex: Au au!
Whiskers: Miau!
Buddy: Au au!
Rex é um cachorro
Whiskers é um gato
Buddy é um cachorro

▶ Exemplo: Modificadores de herança open/final/sealed

KOTLIN
// sealed: subclasses restritas ao mesmo arquivo (usado em when exaustivo)
sealed class Result<out T> {
    data class Success<T>(val data: T) : Result<T>()
    data class Failure(val error: String) : Result<Nothing>()
}

fun handle(result: Result<Int>) = when (result) {
    is Result.Success -> "OK: ${result.data}"
    is Result.Failure -> "Erro: ${result.error}"
    // Compilador sabe que ambos os casos estão cobertos - não precisa de `else`
}

println(handle(Result.Success(42)))
println(handle(Result.Failure("Falha de rede")))

// final (padrão): não pode ser herdada
// open: pode ser herdada
// abstract: deve ser herdada para usar
// sealed: hierarquia fechada (arquivo/módulo)

Saída:

TEXT 📖 Somente leitura
OK: 42
Erro: Falha de rede

▶ Exemplo: Sobrescrita e polimorfismo

KOTLIN
// Métodos da superclasse acessíveis via super
open class Account(val id: String, protected var balance: Double = 0.0) {
    open fun deposit(amount: Double) {
        balance += amount
        println("[$id] Depósito: \$$amount, novo saldo: \$$balance")
    }

    open fun getType() = "Conta"
}

class SavingsAccount(id: String, balance: Double, val interestRate: Double) : Account(id, balance) {
    override fun deposit(amount: Double) {
        super.deposit(amount)
        val interest = amount * interestRate
        balance += interest
        println("[$id] Juros adicionados: \$$interest")
    }

    override fun getType() = "Conta Poupança"
}

val savings = SavingsAccount("SAV-001", 1000.0, 0.005)
savings.deposit(500.0)
println("Tipo: ${savings.getType()}")

Saída:

TEXT 📖 Somente leitura
[SAV-001] Depósito: $500.0, novo saldo: $1500.0
[SAV-001] Juros adicionados: $2.5
Tipo: Conta Poupança

❓ Perguntas Frequentes

P: Por que as classes Kotlin são final por padrão? R: Effective Java recomenda "projetar e documentar para herança, ou proibi-la". O Kotlin adota este conselho, padronizando para proibir herança, usando open para abertura explícita.

P: Interfaces podem ter construtores? R: Não. Interfaces não têm construtores e não podem manter estado. Use classes abstratas para estado compartilhado.

P: Uma classe pode resolver conflitos ao implementar múltiplas interfaces com métodos padrão? R: Sim. Se duas interfaces têm métodos padrão com o mesmo nome e assinatura, a subclasse deve sobrescrever o método e usar super<InterfaceName> para escolher uma implementação.

P: override pode ser omitido? R: Não. O Kotlin exige a palavra-chave override — omiti-la causa um erro de compilação. Este é o mecanismo de segurança chave para prevenir sobrescritas acidentais.

P: Métodos de classes abstratas precisam de open? R: Não. Métodos abstratos em classes abstratas são implicitamente sobrescrevíveis (devem ser sobrescritos). Métodos open não abstratos ainda precisam do modificador open.

P: O Kotlin suporta chamar super como Java? R: Sim. Construtores de subclasse usam super(...) para chamar o construtor pai, métodos usam super.foo() para chamar a implementação pai, e interfaces usam super<Interface>.foo().


📖 Resumo


📝 Exercícios

  1. Iniciante (⭐): Defina um Order e um BulkOrder herdando dele, sobrescrevendo o método summary. Dica: open class Order, class BulkOrder : Order()
  2. Intermediário (⭐⭐): Crie interfaces Discountable e Trackable, faça PriorityOrder implementar ambas, resolvendo um conflito de nome. Dica: super<Discountable>
  3. Desafio (⭐⭐⭐): Projete uma hierarquia de herança de pedidos completa: BaseOrderBulkOrder / PriorityOrder, cada um implementando diferentes estratégias de desconto e envio. Dica: combine interfaces e classes abstratas

← Anterior | Próximo →

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%