O que é Interface em Kotlin?

Resposta rápida: uma interface em Kotlin define um contrato — quais funções e propriedades uma classe precisa ter — sem fixar a implementação. Uma mesma classe pode implementar várias interfaces, e a interface pode trazer métodos com corpo (default methods) e propriedades sem backing field. Para um único método abstrato usável com lambda, declare fun interface. Para hierarquias fechadas e when exaustivo, use sealed interface. Prefira interface quando o foco é o contrato; prefira abstract quando precisa de estado ou construtor compartilhado.

interface Autenticavel {
    fun autenticar(senha: String): Boolean
}

class Usuario(val nome: String, private val senhaHash: String) : Autenticavel {
    override fun autenticar(senha: String): Boolean = senha == senhaHash
}
Se você precisa de…Use
Contrato sem estadointerface
Estado, construtor ou base compartilhadaabstract class
Hierarquia fechada + when exaustivosealed interface / sealed class
Um único método abstrato + lambdafun interface
Reaproveitar implementação prontadelegação com by

Para um passo a passo mais longo, veja o tutorial de interfaces em Kotlin.

Sintaxe básica

interface Autenticavel {
    fun autenticar(senha: String): Boolean
}

class Usuario(val nome: String, private val senhaHash: String) : Autenticavel {
    override fun autenticar(senha: String): Boolean {
        return senha == senhaHash
    }
}

fun main() {
    val user = Usuario("Karina", "abc123")
    println(user.autenticar("abc123")) // true
    println(user.autenticar("errada")) // false
}

Métodos com implementação padrão

Em Kotlin, interfaces podem ter métodos com corpo — os famosos default methods:

interface Logavel {
    val tag: String
        get() = this::class.simpleName ?: "Desconhecido"

    fun log(mensagem: String) {
        println("[$tag] $mensagem")
    }
}

class Servico : Logavel

fun main() {
    val servico = Servico()
    servico.log("Iniciando processamento")
    // [Servico] Iniciando processamento
}

Múltiplas interfaces

interface Imprimivel {
    fun imprimir()
}

interface Exportavel {
    fun exportar(): String
}

class Relatorio(val titulo: String) : Imprimivel, Exportavel {
    override fun imprimir() {
        println("Imprimindo: $titulo")
    }

    override fun exportar(): String {
        return "PDF: $titulo"
    }
}

fun main() {
    val relatorio = Relatorio("Vendas Q1")
    relatorio.imprimir()
    println(relatorio.exportar())
}

Resolvendo conflitos

Quando duas interfaces têm o mesmo método com implementação padrão, você precisa resolver o conflito manualmente:

interface A {
    fun saudacao() = "Olá de A"
}

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

class C : A, B {
    override fun saudacao(): String {
        return "${super<A>.saudacao()} e ${super<B>.saudacao()}"
    }
}

Quando usar interface vs abstract class?

Use interface quando quiser definir um contrato que múltiplas classes podem adotar. Use abstract class quando precisar de estado compartilhado (campos com valores) ou construtores. Interfaces são mais flexíveis e promovem um design mais desacoplado. Detalhes e exemplos no glossário de abstract.

Interface com propriedades abstratas

Interfaces podem declarar propriedades que as classes implementadoras devem fornecer:

interface Identificavel {
    val id: String
    val tipo: String
        get() = "genérico" // Propriedade com valor padrao
}

interface Auditavel {
    val criadoEm: Long
    val atualizadoEm: Long
}

data class Documento(
    override val id: String,
    val titulo: String,
    override val criadoEm: Long,
    override val atualizadoEm: Long
) : Identificavel, Auditavel {
    override val tipo: String
        get() = "documento"
}

fun main() {
    val doc = Documento("doc-001", "Contrato", 1700000000, 1700100000)
    println("${doc.tipo}: ${doc.titulo} (ID: ${doc.id})")
    // documento: Contrato (ID: doc-001)
}

fun interface e conversão SAM

Uma fun interface tem exatamente um método abstrato. Com isso, o Kotlin aceita uma lambda no lugar de uma classe anônima — a conversão SAM (Single Abstract Method):

fun interface Transformador {
    fun transformar(texto: String): String
}

fun aplicar(valor: String, transformador: Transformador): String {
    return transformador.transformar(valor)
}

fun main() {
    val maiusculo = aplicar("kotlin") { it.uppercase() }
    println(maiusculo) // KOTLIN
}

Sem fun interface, interfaces Kotlin não aceitam SAM conversion automaticamente. Interfaces Java com um único método abstrato já permitem a conversão a partir do lado Kotlin.

Interface como tipo para injeção de dependência

Um dos usos mais poderosos de interfaces é permitir a substituição de implementações, fundamental para testes e arquiteturas desacopladas:

interface NotificacaoService {
    fun enviar(destinatario: String, mensagem: String): Boolean
}

class EmailService : NotificacaoService {
    override fun enviar(destinatario: String, mensagem: String): Boolean {
        println("Enviando email para $destinatario: $mensagem")
        return true
    }
}

class SmsService : NotificacaoService {
    override fun enviar(destinatario: String, mensagem: String): Boolean {
        println("Enviando SMS para $destinatario: $mensagem")
        return true
    }
}

// Em testes, voce pode criar um fake facilmente
class FakeNotificacaoService : NotificacaoService {
    val mensagensEnviadas = mutableListOf<Pair<String, String>>()

    override fun enviar(destinatario: String, mensagem: String): Boolean {
        mensagensEnviadas.add(destinatario to mensagem)
        return true
    }
}

class PedidoService(private val notificacao: NotificacaoService) {
    fun finalizarPedido(clienteEmail: String) {
        // ... logica do pedido
        notificacao.enviar(clienteEmail, "Seu pedido foi confirmado!")
    }
}

Delegação de interface com by

Kotlin permite implementar uma interface reaproveitando outra implementação com delegation:

interface Repositorio {
    fun buscar(id: String): String?
    fun salvar(id: String, valor: String)
}

class RepositorioEmMemoria : Repositorio {
    private val dados = mutableMapOf<String, String>()
    override fun buscar(id: String) = dados[id]
    override fun salvar(id: String, valor: String) {
        dados[id] = valor
    }
}

class RepositorioComLog(
    private val origem: Repositorio
) : Repositorio by origem {
    override fun salvar(id: String, valor: String) {
        println("salvando $id")
        origem.salvar(id, valor)
    }
}

Sealed interfaces

A partir do Kotlin 1.5, você pode usar sealed interfaces para restringir quais classes podem implementar uma interface:

sealed interface Resultado {
    data class Sucesso(val dados: String) : Resultado
    data class Erro(val mensagem: String) : Resultado
    data object Carregando : Resultado
}

fun processar(resultado: Resultado) {
    when (resultado) {
        is Resultado.Sucesso -> println("Dados: ${resultado.dados}")
        is Resultado.Erro -> println("Erro: ${resultado.mensagem}")
        Resultado.Carregando -> println("Carregando...")
    }
}

O when é exaustivo automaticamente para sealed interfaces, dispensando o ramo else. Compare com sealed class quando houver estado compartilhado entre os subtipos.

Casos de Uso no Mundo Real

  • Camada de repositório: definir uma interface UsuarioRepository com operações como buscarPorId, salvar e deletar, permitindo trocar a implementação entre banco de dados real e repositório em memória para testes.
  • Strategy pattern em regras de negócio: criar uma interface CalculoFrete com diferentes implementações para Correios, transportadora e retirada, injetando a estratégia correta conforme a escolha do cliente.
  • Plugins e extensibilidade: sistemas de plugins usam interfaces para definir contratos que módulos externos devem seguir, permitindo adicionar funcionalidades sem modificar o código principal.
  • Tratamento de estado com sealed interface: modelar estados de tela no Android (Loading, Success, Error) como uma sealed interface, garantindo que o when cubra todos os estados possíveis.
  • Callbacks concisos com fun interface: APIs de listener no Android e no backend aceitam lambda em vez de classe anônima verbosa.

Boas Práticas

  • Prefira interfaces pequenas e focadas (Interface Segregation Principle). Uma interface com muitos métodos força implementadores a lidar com funcionalidades que podem não ser relevantes.
  • Use implementações padrão com moderação. Elas são úteis para métodos auxiliares, mas não devem conter lógica de negócio complexa que seria melhor em uma classe abstrata.
  • Nomeie interfaces pelo comportamento que descrevem, não pela implementação: Serializavel, Persistivel, Autenticavel comunicam intenção melhor que SerializadorImpl ou BancoDeDados.
  • Programe para a interface, não para a implementação. Declare variáveis e parâmetros usando o tipo da interface sempre que possível.
  • Use sealed interfaces quando o conjunto de implementações é fechado e conhecido, permitindo when exaustivo e maior segurança em tempo de compilação.
  • Marque como fun interface só quando houver exatamente um método abstrato e a API for pensada para receber lambdas.

Erros Comuns

  • Criar interfaces com um único implementador sem justificativa: se a interface existe apenas por existir e nunca será reimplementada, ela adiciona complexidade sem benefício. Crie interfaces quando houver necessidade real de abstração.
  • Colocar estado mutável em interfaces: interfaces não devem ter campos var com backing field. Se precisa de estado compartilhado, use uma classe abstrata.
  • Esquecer o override ao implementar métodos da interface: diferente de Java, Kotlin exige a palavra-chave override explicitamente, o que previne erros acidentais.
  • Não resolver conflitos de nomes entre interfaces: quando duas interfaces têm métodos com o mesmo nome e implementação padrão, a classe deve resolver explicitamente com super<Interface>.método().
  • Usar interface quando abstract class seria mais apropriada: se as implementações compartilham estado (propriedades com backing field) ou lógica de construtor, uma classe abstrata é a escolha mais adequada.
  • Esperar SAM conversion em interface Kotlin comum: sem fun interface, passar uma lambda no lugar da interface gera erro de tipo.

Perguntas Frequentes

Interfaces em Kotlin podem ter construtores? Não. Interfaces não podem ter construtores. Se você precisa de parâmetros de inicialização, use uma classe abstrata.

Qual a diferença entre interface em Kotlin e em Java? Em Kotlin, interfaces podem ter propriedades (sem backing field), implementações padrão de métodos e participar de sealed hierarchies. Em Java, default methods existem desde o Java 8, mas propriedades em interfaces não são suportadas da mesma forma. Para SAM conversion em interfaces Kotlin, use fun interface.

Posso usar delegação com interfaces? Sim. Kotlin suporta delegação de interface com a palavra-chave by: class MeuServico(servico: NotificacaoService) : NotificacaoService by servico. Isso delega todas as chamadas de interface para o objeto fornecido. Veja delegation.

Quando usar sealed interface vs sealed class? Use sealed interface quando as implementações não precisam compartilhar estado (propriedades com backing field) e quando deseja que uma classe implemente múltiplas sealed hierarchies. Sealed class é melhor quando há estado e lógica compartilhados.

O que é fun interface? É a forma de declarar uma interface funcional em Kotlin, com um único método abstrato, habilitando conversão automática de lambda (SAM).

Termos Relacionados

  • Abstract — alternativa quando há estado ou construtor compartilhado
  • Sealed Class — hierarquias fechadas; base para comparar com sealed interface
  • Delegation — reaproveitar implementação de interface com by
  • Fun — funções declaradas em interfaces são implementadas usando fun e override
  • Generics — interfaces genéricas como Comparable<T> e Iterable<T> definem contratos flexíveis
  • Lambda — fun interface permite substituir a implementação por lambda (SAM)
  • Higher-Order Function — interfaces com um único método abstrato se aproximam de tipos de função
  • Tutorial: Interfaces em Kotlin — guia passo a passo com mais exemplos