extension property — um mecanismo do Kotlin que permite adicionar novas propriedades a classes existentes sem herança e sem modificar o código-fonte. Ao contrário das extension functions, as extension properties não podem armazenar estado — são declaradas apenas com um getter e, opcionalmente, um setter, pois não possuem backing field. De acordo com Kotlin Documentation, 2025, as extension properties são compiladas em métodos estáticos getter e setter com o receiver como primeiro parâmetro.
Principais pontos
Uma extension property é uma construção sintática do Kotlin que adiciona uma propriedade a um tipo existente sem alterar sua declaração. A propriedade é declarada com um receiver type e deve conter um getter. A diferença chave das propriedades normais é a ausência de backing field: uma extension property não pode armazenar dados, apenas calculá-los com base no objeto receiver.
De acordo com a Pesquisa Kotlin Foundation (2024), as extension properties são menos populares que as extension functions — cerca de 45% dos desenvolvedores as usam regularmente. Isso se deve à limitação de não ter estado, o que reduz o escopo de aplicação. No entanto, para propriedades calculadas que estão logicamente ligadas a um tipo, as extension properties são a opção mais concisa.
As extension properties são compiladas em um par de métodos estáticos getter e setter. No nível de bytecode, não há diferença entre chamar uma extension property e chamar uma extension function — ambas se tornam métodos estáticos com um parâmetro receiver. De acordo com JetBrains (Kotlin Docs, 2025), não há nenhuma sobrecarga.
Use extension properties para valores calculados curtos que devem parecer propriedades em vez de chamadas de método — isso melhora a legibilidade do código e segue o Princípio de Acesso Uniforme.
Para declarar uma extension property, use uma sintaxe semelhante a uma propriedade normal, mas com o prefixo do receiver type. val declara uma extension property somente leitura com um getter obrigatório, var declara uma mutável com um getter e um setter opcional.
// Extension property somente leitura
val String.isEmail: Boolean
get() = this.contains("@") && this.contains(".")
// Chamada
val valid = "test@test.com".isEmail
Nota: uma extension property é chamada sem parênteses — str.isEmail, não str.isEmail(). Esta é a diferença chave entre uma extension property e uma extension function: uma propriedade parece um campo, embora na realidade seja calculada através de um getter.
Extension properties podem ser genéricas — o receiver pode usar parâmetros de tipo genérico. Isso permite criar propriedades universais que funcionam com qualquer tipo de coleção.
val List<T>.secondOrNull: T?
get() = if (size >= 2) this[1] else null
val items = listOf("a", "b", "c")
val second = items.secondOrNull // "b"
A propriedade secondOrNull funciona para qualquer tipo T, retornando o segundo elemento da lista ou null se houver menos de dois elementos. Este é um exemplo típico onde uma extension property é mais apropriada que uma função — seu acesso parece a leitura de um campo.
Uma extension property não pode ter um backing field porque não é adicionada aos metadados da classe — existe apenas como um par de funções estáticas getter/setter. Backing field (a palavra-chave field em Kotlin) é um campo interno da classe que armazena o valor da propriedade. Uma extension property não tem acesso à estrutura interna da classe.
// ❌ ERRO: extension property não pode ter um backing field
var String.cachedValue: String
get() = "computed"
set(value) {
field = value // campo não está acessível!
}
// ✅ CORRETO: use armazenamento externo
val cache = MutableMap<String, String>()
var String.cachedValue: String
get() = cache[this] ?: ""
set(value) { cache[this] = value }
Um Map externo no exemplo resolve o problema de armazenamento mas cria outro — um vazamento de memória. Os valores obtidos através de uma extension property vivem no Map para sempre se não forem limpos. Essa limitação torna as extension properties inadequadas para cache ou armazenamento de dados temporários.
Para cache, é recomendado usar um WeakHashMap ou mecanismos com limpeza automática. JetBrains recomenda evitar o uso de var extension properties com armazenamento externo em código de produção sem gerenciamento cuidadoso do ciclo de vida.
A escolha entre uma extension property e uma extension function depende da semântica: uma propriedade descreve uma característica de um objeto, enquanto uma função descreve uma ação. O Princípio de Acesso Uniforme estabelece: o cliente não deve saber se um valor é calculado ou armazenado. Se o valor pode ser representado como uma característica (comprimento, tamanho, status) — use uma propriedade.
| Critério | Extension property | Extension function |
|---|---|---|
| Chamada | Sem parênteses: obj.property | Com parênteses: obj.function() |
| Semântica | Característica, atributo | Ação, operação |
| Backing field | Não suportado | Não aplicável |
| Parâmetros | Apenas getter/setter | Quaisquer parâmetros |
| Desempenho | Mesmo (método estático) | Mesmo (método estático) |
| Exemplo | text.length | text.isEmail() |
A regra é simples: se a operação aceita parâmetros — use uma extension function. Se é um valor calculado simples sem parâmetros — use uma extension property. De acordo com Android Architecture Guide (Google, 2025), deve-se dar preferência a extension properties para acesso a dados e extension functions para operações com efeitos colaterais.
Uma extension property com a palavra-chave var suporta um setter, mas sem a capacidade de armazenar um valor — o setter geralmente realiza um efeito colateral ou salva dados em um armazenamento externo. A sintaxe é semelhante às propriedades mutáveis de classe.
// Extension property mutável com setter
var StringBuilder.lastChar: Char
get() = this[length - 1]
set(value) {
this.setCharAt(length - 1, value)
}
val sb = StringBuilder("Kotlin")
println(sb.lastChar) // n
sb.lastChar = '!'
println(sb) // Kotli!
A propriedade lastChar é um exemplo clássico da documentação do Kotlin. O getter retorna o último caractere do StringBuilder, o setter o substitui por um novo valor. Nota: o estado é armazenado no próprio StringBuilder (via setCharAt), não em um campo separado — este é um uso correto de uma extension property.
Em projetos reais, as extension properties são mais frequentemente usadas para simplificar o acesso a dados de coleções, calcular tamanhos ou status de elementos de UI e criar uma API conveniente sobre classes existentes. A biblioteca padrão do Kotlin usa ativamente este mecanismo: size, indices, lastIndex para coleções são extension properties.
// Extension properties para coleções
val List<Int>.sumFast: Int
get() = fold(0) { acc, i -> acc + i }
val String.half: String
get() = this.substring(0, length / 2)
// Extension property para Android View
val View.isVisible: Boolean
get() = visibility == View.VISIBLE
// Verificação nula via receiver seguro
val String?.isNullOrBlank: Boolean
get() = this == null || this.isBlank()
A extension property isVisible para View é um exemplo que todo desenvolvedor Android deveria conhecer. Em vez de view.visibility == View.VISIBLE, você pode escrever view.isVisible. Isso não é apenas mais curto, mas também é lido como linguagem natural: "se a view está visível". Apesar da simplicidade, tais propriedades melhoram significativamente a legibilidade do código.
Perguntas frequentes
Não, extension properties não podem ser declaradas para um companion object ou object declaration. O mecanismo de extensão se aplica apenas a classes, interfaces e tipos anuláveis. Para object, use funções regulares de nível superior.
Uma inline property (com o modificador inline) é um mecanismo do Kotlin para chamar um getter/setter sem criar um objeto de propriedade. Uma extension property é sempre compilada em um método estático, enquanto uma inline property é compilada em uma chamada sem wrapper. Elas resolvem problemas diferentes: uma extension property adiciona uma propriedade a um tipo existente, enquanto inline otimiza chamadas para suas próprias propriedades.
Sim, uma extension property pode ter anotações, mas apenas no nível de declaração. Não é possível anotar separadamente o getter ou setter de uma extension property — ao contrário das propriedades de classe normais. Exemplo: @JvmName("getIsValid") val String.isValid get() = true.
Não, extension properties não podem ser declaradas com um companion object como receiver. Esta é uma limitação da linguagem — uma extension property funciona apenas com instâncias de tipo, enquanto um companion object é um contexto estático. Use funções de extensão de nível superior ou constantes.
Minimamente. Cada extension property adiciona um método getter estático (e opcionalmente um setter) ao bytecode compilado. Para comparação, criar uma classe wrapper com a mesma propriedade adiciona uma classe inteira. Extension properties são uma abordagem mais leve para estender funcionalidades.
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