extension function — um mecanismo do Kotlin que permite adicionar novos métodos a classes existentes sem herança e sem modificar o código fonte. A função é declarada com um prefixo na forma de um tipo receptor e é chamada como um método normal desse tipo. De acordo com Kotlin Documentation, 2025, as extension functions são compiladas como métodos estáticos com o receptor como primeiro parâmetro, o que garante zero overhead em tempo de execução comparado a chamadas normais.
Principais pontos
Uma extension function é uma função declarada fora de uma classe, mas chamada como seu método. Ela recebe um tipo receptor ao qual o novo método é adicionado. Dentro da função, a palavra-chave this refere-se à instância do tipo receptor. Essa abordagem permite estender classes finais, classes de bibliotecas de terceiros e até tipos primitivos sem criar wrappers.
Ao contrário do Java, onde adicionar um método a uma classe existente exige herança ou uma classe utilitária estática, o Kotlin oferece uma solução elegante sem boilerplate. De acordo com um estudo da Kotlin Foundation (2024), as extension functions estão entre as cinco principais funcionalidades mais usadas da linguagem — 78% dos desenvolvedores Kotlin as utilizam no trabalho diário.
O compilador Kotlin traduz uma extension function em um método estático onde a instância do receptor é passada como primeiro argumento. Isso significa que extension functions não modificam o bytecode da classe alvo nem quebram o encapsulamento — apenas campos e métodos públicos do receptor são acessíveis.
Use extension functions para escrever funções utilitárias limpas que estão logicamente vinculadas a um tipo específico, mas não podem ser adicionadas ao seu código fonte.
A sintaxe básica de uma extension function: o nome do tipo receptor, um ponto, o nome da função, depois os parâmetros e o tipo de retorno. Dentro da função, o receptor é acessível via this.
// Extension function for String
fun String.isEmail(): Boolean {
return this.contains("@") && this.contains(".")
}
// Called as regular String method
val result = "user@example.com".isEmail() // true
Neste exemplo, isEmail() fica disponível para todas as strings. Dentro da função, this refere-se à própria string na qual o método é chamado. Kotlin permite omitir this na maioria dos casos — você pode escrever apenas contains("@") em vez de this.contains("@").
Extension functions podem aceitar parâmetros adicionais e retornar valores de qualquer tipo. Isso as torna funções completas, não apenas açúcar sintático.
fun List<Int>.defaultIfEmpty(default: Int): List<Int> {
if (this.isEmpty()) return listOf(default)
return this
}
val populated = listOf(1, 2).defaultIfEmpty(0)
val empty = listOf<Int>().defaultIfEmpty(0)
O parâmetro default define o valor retornado para uma lista vazia. Uma extension function pode ser genérica — o tipo List
Extension functions em Kotlin usam despacho estático, não despacho virtual. Esta é uma diferença chave dos métodos de classe normais. Qual extension function será chamada é determinado em tempo de compilação pelo tipo estático da variável, não pelo seu tipo real em tempo de execução.
open class Animal
class Dog : Animal()
fun Animal.speak() = "Animal sound"
fun Dog.speak() = "Woof"
fun test() {
val animal: Animal = Dog()
println(animal.speak()) // "Animal sound" — static type Animal
val dog: Dog = Dog()
println(dog.speak()) // "Woof" — static type Dog
}
Mesmo que a variável animal aponte para uma instância de Dog, a extension function para Animal é chamada porque o tipo estático da variável é Animal. Se speak() fosse um método virtual de classe, Dog.speak() teria sido chamado. Este comportamento é importante considerar ao projetar APIs com extension functions.
Extension functions não são membros da classe — são funções normais que exigem importação para serem usadas em outro arquivo. Kotlin oferece duas opções de importação: por nome da função ou com renomeação.
// Import by name
import com.example.extensions.isEmail
// Import with alias (to resolve conflicts)
import com.example.extensions.isEmail as isValidEmail
// Call after import
val valid = "test@test.com".isEmail()
A renomeação via as é útil para conflitos de nomes — por exemplo, se duas bibliotecas fornecem uma extension function com o mesmo nome para o mesmo tipo. Neste caso, você pode importar uma delas com um nome diferente e chamá-la usando o novo nome.
Extension functions podem ser declaradas em diferentes níveis:
| Nível | Visibilidade | Exemplo |
|---|---|---|
| Top-level | Projeto inteiro após importação | fun String.isEmail() |
| Member extension | Dentro da classe proprietária | class A { fun B.ext() } |
| Local | Dentro de uma função | fun test() { fun String.ext() } |
As member extension functions são um caso especial onde uma extension function é declarada dentro de outra classe. Neste caso, tanto o receptor (this da função) quanto os membros da classe externa são acessíveis dentro da extension function.
Se uma classe tem um método com a mesma assinatura de uma extension function, o membro da classe é sempre chamado. Uma extension function nunca sobrescreve um método de classe — esta é uma decisão arquitetural para evitar sobrescritas acidentais.
class User {
fun greet() = "Hello from class"
}
fun User.greet() = "Hello from extension"
fun main() {
val user = User()
println(user.greet()) // "Hello from class"
}
Mesmo se a extension function for definida depois e tiver a mesma assinatura, o compilador escolherá o método da classe. O compilador Kotlin emite um aviso ao encontrar esta situação. A única maneira de chamar a extension function quando existe um método de classe é invocá-la como uma função normal: greet(user).
No desenvolvimento Android, as extension functions se tornaram uma ferramenta padrão para trabalhar com View, Context e fragments. A biblioteca Android KTX é construída sobre extension functions, fornecendo wrappers convenientes sobre a API do Android.
// Extension function for working with View
fun View.show() {
visibility = View.VISIBLE
}
fun View.hide() {
visibility = View.GONE
}
// Extension function for Context — toast
fun Context.toast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
// Extension function as DSL builder
fun String.colorFormat(color: String): String =
"$color$this\u001b[0m"
A extension function toast() torna as chamadas dependentes de Context concisas: em vez de escrever Toast.makeText(context, message, length).show(), basta escrever context.toast("Texto"). Isso reduz o boilerplate e torna o código mais legível.
Perguntas frequentes
Não, extension functions usam despacho estático. Se você declarar uma extension function para uma classe base e a mesma para uma subclasse, qual será chamada é determinada pelo tipo estático da variável em tempo de compilação, não pelo tipo real em tempo de execução.
Você pode declarar extension functions para tipos receptores nullable: fun String?.isNullOrEmail(). Dentro dessa função, this pode ser null, então você deve usar operadores de chamada segura ou verificações explícitas de null.
Não, extension functions são compiladas como métodos estáticos. No nível de bytecode, chamar uma extension function é idêntico a chamar um método estático com o receptor como primeiro parâmetro. Não envolve reflexão ou despacho dinâmico.
Sim, extension properties funcionam de forma similar às extension functions, mas não podem armazenar estado — apenas um getter e um setter. Por exemplo: val List
Em Java você escreve StringUtils.isEmail(str), enquanto em Kotlin você escreve str.isEmail(). A diferença não é apenas sintática: extension functions suportam autocompletar no IDE, melhoram a legibilidade de cadeias de chamadas e permitem que o IDE sugira funções relevantes para um tipo específico. Java não possui essas capacidades.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também