O que e Extension Function em Kotlin?
Extension Functions permitem adicionar novas funções a classes existentes sem precisar herdar delas ou usar padrões como Decorator. Voce estende o comportamento de qualquer classe – inclusive as que não sao suas, como String, Int ou List.
E uma das funcionalidades mais elegantes do Kotlin é uma das que mais faz a galera se apaixonar pela linguagem.
Imagine que você comprou uma estante pronta. Voce não pode modificar o projeto original da fabrica, mas pode parafusar uma prateleira extra nela. Extension functions fazem isso com classes: adicionam comportamento novo sem alterar o código original. A classe não sabe que foi estendida, mas quem usa ganha uma função nova disponivel como se fosse nativa.
Esse recurso e fundamental para criar DSLs, APIs fluentes e utilitarios reutilizaveis. A própria biblioteca padrão do Kotlin e repleta de extension functions – funções como let, also, map e filter em colecoes sao todas extensions.
Sintaxe básica
fun String.contarPalavras(): Int {
return this.trim().split("\\s+".toRegex()).size
}
fun main() {
val frase = "Kotlin e muito massa"
println(frase.contarPalavras()) // 4
}
A função contarPalavras() agora pode ser chamada em qualquer String, como se fizesse parte da classe original. O this dentro da função se refere ao objeto em que ela foi chamada.
Extension em tipos numericos
fun Double.formatarReais(): String {
return "R$ ${"%.2f".format(this)}"
}
fun Int.ehPar(): Boolean = this % 2 == 0
fun main() {
println(1599.90.formatarReais()) // R$ 1599.90
println(42.ehPar()) // true
println(7.ehPar()) // false
}
Extension em colecoes
fun <T> List<T>.segundoOuNull(): T? {
return if (this.size >= 2) this[1] else null
}
fun main() {
val nomes = listOf("Ana", "Bruno", "Carlos")
println(nomes.segundoOuNull()) // Bruno
val vazia = emptyList<String>()
println(vazia.segundoOuNull()) // null
}
Como funciona por tras
Na real, extension functions sao resolvidas estaticamente. O compilador transforma a função numa função estática com o objeto como primeiro parametro. Isso significa que elas não suportam polimorfismo – e a classe declarada que conta, não a real.
open class Animal
class Cachorro : Animal()
fun Animal.som() = "..."
fun Cachorro.som() = "Au au!"
fun fazerSom(animal: Animal) = animal.som()
fun main() {
println(fazerSom(Cachorro())) // "..." -- usa o tipo declarado
}
Extension Properties
Alem de funções, você pode criar propriedades de extensao. Elas não armazenam estado (não tem backing field), mas podem calcular valores:
val String.primeiraLetraMaiuscula: String
get() = if (isNotEmpty()) {
this[0].uppercase() + substring(1)
} else {
this
}
val List<Int>.media: Double
get() = if (isNotEmpty()) sum().toDouble() / size else 0.0
fun main() {
println("kotlin".primeiraLetraMaiuscula) // Kotlin
println(listOf(10, 20, 30).media) // 20.0
}
Extension Functions com generics
Extensions combinadas com generics permitem criar funções reutilizaveis para qualquer tipo:
fun <T> T.tambemLogar(tag: String = "LOG"): T {
println("[$tag] $this")
return this
}
fun <T> List<T>.agruparEmPares(): List<List<T>> {
return chunked(2)
}
fun main() {
val resultado = 42.tambemLogar("Numero") // [Numero] 42
println(resultado) // 42
val pares = listOf("A", "B", "C", "D", "E").agruparEmPares()
println(pares) // [[A, B], [C, D], [E]]
}
Extension Functions em tipos nullable
Voce pode criar extensions que funcionam mesmo quando o objeto e null:
fun String?.ouPadrao(padrao: String = "N/A"): String {
return this ?: padrao
}
fun main() {
val nome: String? = null
val apelido: String? = "Kari"
println(nome.ouPadrao()) // N/A
println(apelido.ouPadrao()) // Kari
println(nome.ouPadrao("Sem nome")) // Sem nome
}
Casos de Uso no Mundo Real
- Formatacao de dados: criar extensions como
Date.formatarBrasileiro()ouDouble.formatarMoeda()que encapsulam regras de formatacao usadas em todo o projeto. - Validacao: funções como
String.ehCpfValido()ouString.ehEmailValido()que centralizam lógica de validacao e podem ser chamadas de qualquer lugar. - Android e Jetpack: extensions como
View.esconder(),View.mostrar(),Context.toast()sao extremamente comuns em projetos Android para reduzir boilerplate. - Conversao de tipos: funções como
String.toUsuario()ouMap.toConfig()que transformam dados brutos em objetos de dominio usando data classes. - Testes: criar extensions que facilitam assercoes, como
List.deveConter(item)ouString.deveSerVazia().
Boas Praticas
- Use extension functions para adicionar comportamento a classes que você não controla. Se você controla a classe, avalie se a função não deveria ser um método normal.
- Mantenha extensions em arquivos organizados por tipo (por exemplo,
StringExtensions.kt,ListExtensions.kt). Isso facilita a descoberta e reutilização. - Evite criar extensions que dependem de estado externo ou efeitos colaterais. Extensions devem ser previsiveis e puras sempre que possível.
- Prefira extensions a funções utilitarias com o objeto como parametro.
email.ehValido()e mais natural queehValido(email). - Documente suas extensions, especialmente as que serao usadas por outros desenvolvedores. O autocomplete da IDE mostra extensions, mas sem documentação pode ser difícil entender o que cada uma faz.
Erros Comuns
- Esperar comportamento polimorfico: como extensions sao resolvidas estaticamente, chamar
animal.som()sempre usa o tipo declarado da variavel, não o tipo real do objeto. Se precisar de polimorfismo, use métodos de instancia ou interfaces. - Criar extensions demais: adicionar dezenas de extensions a
StringouListpolui o autocomplete da IDE e dificulta a manutenção. Seja seletivo. - Conflito com métodos existentes: se uma extension tem a mesma assinatura que um método da classe, o método da classe sempre vence. A extension nunca sera chamada, sem nenhum aviso do compilador.
- Nao importar a extension: extensions precisam ser importadas para serem usadas fora do arquivo onde foram declaradas. Esquecimento de import e um erro frequente.
Perguntas Frequentes
Extension functions alteram a classe original? Nao. A classe original permanece intacta. O compilador transforma a extension em uma função estática que recebe o objeto como primeiro parametro. E apenas acucar sintatico.
Posso criar extension functions para companion objects?
Sim. Voce pode estender o companion object de uma classe, permitindo chamar a extension como se fosse uma função estática: MinhaClasse.minhaExtension().
Extensions funcionam em Kotlin Multiplatform? Sim. Extensions sao um recurso da linguagem Kotlin e funcionam em todas as plataformas: JVM, JS e Native. Elas sao especialmente úteis para criar APIs consistentes entre plataformas usando expect/actual.
Qual a diferenca entre extension function e higher-order function?
Sao conceitos diferentes. Extension functions adicionam funções a tipos existentes. Higher-order functions recebem ou retornam funções como parametro. Porem, elas se combinam muito bem – por exemplo, List.filter {} e uma extension function que recebe uma lambda como parametro.
Quando usar? Use extension functions para adicionar utilidades a classes que você não controla, criar APIs mais fluentes e manter o código organizado. A biblioteca padrão do Kotlin e cheia delas.