---
title: "Extension Functions em Kotlin — Tutorial em Português | Kotlin Brasil"
url: "https://kotlin.dev.br/blog/extension-functions-kotlin/"
markdown_url: "https://kotlin.dev.br/blog/extension-functions-kotlin.MD"
description: "Aprenda Extension Functions em Kotlin com exemplos práticos. Tutorial completo em português sobre como estender classes sem herança."
date: "2026-03-06"
author: "Karina Melo"
---

# Extension Functions em Kotlin — Tutorial em Português | Kotlin Brasil

Aprenda Extension Functions em Kotlin com exemplos práticos. Tutorial completo em português sobre como estender classes sem herança.


Imagine poder adicionar métodos novos a classes que já existem — sem herança, sem wrappers, sem modificar o código original. Parece mágica? Em Kotlin, isso se chama **Extension Functions**, e é um dos recursos mais queridos da linguagem.

## O que são Extension Functions?

Extension Functions permitem que você "estenda" uma classe com novas funções, como se elas fizessem parte da classe original. A sintaxe é simples:

```kotlin
fun String.contarPalavras(): Int {
    return this.trim().split("\\s+".toRegex()).size
}

fun main() {
    val frase = "Kotlin é incrível demais da conta"
    println(frase.contarPalavras()) // 6
}
```

Repare: `contarPalavras()` não existe na classe `String`, mas a gente chamou como se existisse. O `this` dentro da função refere-se ao objeto receptor (no caso, a String).

## Exemplos práticos do dia a dia

Vamos ver extensões que você vai querer copiar agora mesmo pro seu projeto:

### Formatação de valores brasileiros

```kotlin
fun Double.toBRL(): String {
    return "R$ %,.2f".format(this).replace(",", "X").replace(".", ",").replace("X", ".")
}

fun String.toCpfFormatado(): String {
    val limpo = this.replace("[^0-9]".toRegex(), "")
    require(limpo.length == 11) { "CPF deve ter 11 dígitos" }
    return "${limpo.substring(0,3)}.${limpo.substring(3,6)}.${limpo.substring(6,9)}-${limpo.substring(9)}"
}

fun String.toCnpjFormatado(): String {
    val limpo = this.replace("[^0-9]".toRegex(), "")
    require(limpo.length == 14) { "CNPJ deve ter 14 dígitos" }
    return "${limpo.substring(0,2)}.${limpo.substring(2,5)}.${limpo.substring(5,8)}/${limpo.substring(8,12)}-${limpo.substring(12)}"
}

fun main() {
    println(1499.90.toBRL())                     // R$ 1.499,90
    println("12345678901".toCpfFormatado())       // 123.456.789-01
    println("12345678000190".toCnpjFormatado())   // 12.345.678/0001-90
}
```

### Validações

```kotlin
fun String.isEmailValido(): Boolean {
    return matches("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}".toRegex())
}

fun String.isCpfValido(): Boolean {
    val numeros = this.replace("[^0-9]".toRegex(), "")
    if (numeros.length != 11) return false
    if (numeros.all { it == numeros[0] }) return false

    // Validação dos dígitos verificadores
    val dv1 = (0..8).sumOf { (10 - it) * numeros[it].digitToInt() } % 11
    val primeiroDigito = if (dv1 < 2) 0 else 11 - dv1

    val dv2 = (0..9).sumOf { (11 - it) * numeros[it].digitToInt() } % 11
    val segundoDigito = if (dv2 < 2) 0 else 11 - dv2

    return numeros[9].digitToInt() == primeiroDigito &&
           numeros[10].digitToInt() == segundoDigito
}

fun main() {
    println("karina@kotlin.dev.br".isEmailValido()) // true
    println("email-invalido".isEmailValido())        // false
}
```

### Manipulação de datas

```kotlin
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.time.Period

fun LocalDate.toBrazilianFormat(): String {
    return this.format(DateTimeFormatter.ofPattern("dd/MM/yyyy"))
}

fun LocalDate.idadeEmAnos(): Int {
    return Period.between(this, LocalDate.now()).years
}

fun main() {
    val nascimento = LocalDate.of(1995, 6, 15)
    println(nascimento.toBrazilianFormat()) // 15/06/1995
    println("Idade: ${nascimento.idadeEmAnos()} anos")
}
```

## Extension Properties

Não é só função — você também pode criar propriedades de extensão:

```kotlin
val String.primeiraLetra: Char
    get() = this.first()

val String.ultimaLetra: Char
    get() = this.last()

val List<*>.penultimoElemento: Any?
    get() = this[this.size - 2]

fun main() {
    println("Kotlin".primeiraLetra)  // K
    println("Kotlin".ultimaLetra)    // n
}
```

Uma limitação importante: extension properties não podem ter backing field, ou seja, não podem armazenar estado. Elas sempre precisam ser calculadas.

## Extensions em coleções

Extensões brilham quando trabalham com coleções:

```kotlin
fun <T> List<T>.segundoOuNull(): T? = if (this.size >= 2) this[1] else null

fun List<Int>.mediana(): Double {
    val ordenada = this.sorted()
    val meio = ordenada.size / 2
    return if (ordenada.size % 2 == 0) {
        (ordenada[meio - 1] + ordenada[meio]) / 2.0
    } else {
        ordenada[meio].toDouble()
    }
}

fun <T> List<T>.agruparEmPares(): List<Pair<T, T?>> {
    return this.chunked(2).map { chunk ->
        Pair(chunk[0], chunk.getOrNull(1))
    }
}

fun main() {
    val numeros = listOf(3, 1, 4, 1, 5, 9, 2, 6)
    println(numeros.mediana())          // 3.5
    println(numeros.segundoOuNull())    // 1
    println(numeros.agruparEmPares())   // [(3, 1), (4, 1), (5, 9), (2, 6)]
}
```

## Como funciona por baixo dos panos

É importante entender: extension functions **não modificam realmente a classe**. Elas são resolvidas estaticamente. No bytecode, uma extension function vira uma função estática onde o primeiro parâmetro é o objeto receptor:

```kotlin
// Isso:
fun String.gritando() = this.uppercase() + "!!!"

// Vira essencialmente isso no bytecode:
// public static String gritando(String $this) { return $this.toUpperCase() + "!!!"; }
```

Isso tem implicações importantes:

- **Não há polimorfismo**: se uma classe filha e uma classe pai têm extensions com o mesmo nome, o tipo declarado (não o real) determina qual é chamada
- **Membros sempre ganham**: se a classe já tem um método com o mesmo nome, o membro da classe vence

```kotlin
class Mensagem(val texto: String) {
    fun exibir() = "Classe: $texto"
}

fun Mensagem.exibir() = "Extension: $texto" // NUNCA será chamada

fun main() {
    println(Mensagem("Oi").exibir()) // "Classe: Oi" — o membro vence
}
```

## Organizando extensions

Em projetos maiores, organize suas extensions em arquivos dedicados:

```
src/
  extensions/
    StringExtensions.kt
    DateExtensions.kt
    CollectionExtensions.kt
    ViewExtensions.kt  // Android
```

```kotlin
// StringExtensions.kt
package br.dev.kotlin.extensions

fun String.capitalizar(): String {
    return this.split(" ").joinToString(" ") { palavra ->
        palavra.replaceFirstChar { it.uppercase() }
    }
}
```

## Boas práticas

1. **Use extensions para adicionar funcionalidade coesa** a uma classe
2. **Não abuse**: se a lógica é complexa, talvez uma classe própria faça mais sentido
3. **Dê nomes descritivos**: a extension deve parecer um membro natural da classe
4. **Documente**: extensions podem surpreender quem lê o código pela primeira vez
5. **Agrupe por tipo**: mantenha as extensions organizadas em arquivos temáticos

## Conclusão

Extension Functions são um dos recursos que fazem Kotlin ser tão expressivo e prazeroso de programar. Elas permitem que você adapte a linguagem ao seu domínio, criando uma API fluente e natural. Conceitos similares existem em outras linguagens: <a href="https://rustlang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust tem trait implementations para tipos externos</a>, e <a href="https://golang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go usa funções com receivers para um efeito parecido</a>. Use com sabedoria, e seu código vai ficar muito mais legível e manutenível.

Bora estender tudo!
