As scope functions em Kotlin são cinco funções da biblioteca padrão (let, run, with, apply, also) que executam um bloco de código no contexto de um objeto. Elas diferem na forma de acessar o objeto (através de it ou this) e no valor de retorno (objeto de contexto ou resultado da lambda). De acordo com a documentação do Kotlin (2026), a escolha correta da scope function reduz o código repetitivo em 25–40%. As scope functions ajudam a escrever inicialização, configuração e transformações de objetos em um estilo declarativo.
Pontos principais
As scope functions são um conjunto de cinco funções (let, run, with, apply, also) integradas à biblioteca padrão do Kotlin. Cada uma delas recebe uma lambda e altera temporariamente o escopo para que, dentro da lambda, o código execute no contexto de um objeto específico. Isso elimina a repetição do nome do objeto para múltiplas chamadas e agrupa operações relacionadas em um único bloco.
Duas características principais diferenciam as scope functions: a forma de acessar o objeto de contexto (através de this ou através de it) e o valor de retorno (o próprio objeto de contexto ou o resultado da lambda). O acesso via this torna o objeto um receptor implícito — métodos e propriedades são chamados sem prefixo. O acesso via it passa o objeto como um parâmetro explícito da lambda. Retornar o objeto permite encadeamento; retornar o resultado permite atribuir o valor transformado.
Historicamente, as scope functions surgiram como uma substituição para o padrão clássico Builder e variáveis temporárias para inicialização. Em vez de criar uma classe builder separada, o desenvolvedor escreve apply { propriedade1 = ...; propriedade2 = ... }. Isso reduz a quantidade de código e torna a inicialização mais legível. No ecossistema Kotlin, as scope functions são usadas em todos os frameworks e bibliotecas como uma expressão idiomática padrão da linguagem.
A escolha da scope function correta é determinada por duas perguntas: se você deseja usar this ou it para acessar o objeto, e se você deseja retornar o próprio objeto ou o resultado da lambda. A combinação de duas características binárias dá quatro opções possíveis, e with ocupa um lugar especial como a única função que não é uma extensão.
| Função | Acesso | Retorno | Extensão? | Cenário típico |
|---|---|---|---|---|
| let | it | resultado da lambda | sim | Transformação, verificação null |
| run | this | resultado da lambda | sim | Cálculo com contexto |
| with | this | resultado da lambda | não | Agrupamento de chamadas |
| apply | this | objeto de contexto | sim | Inicialização de propriedades |
| also | it | objeto de contexto | sim | Efeitos colaterais, registro |
Para configurar um objeto sem retornar um novo valor, use apply (acesso via this) ou also (acesso via it). Para calcular um novo valor baseado em um objeto, use let (acesso via it) ou run (acesso via this). Para agrupar chamadas sem encadeamento, use with. Seguir este esquema torna o código previsível para outros desenvolvedores familiarizados com Kotlin.
apply é uma scope function que retorna o objeto de contexto e fornece acesso a ele via this. É a ferramenta ideal para inicializar propriedades de um objeto após sua criação. Dentro do bloco apply, as propriedades podem ser definidas sem repetir o nome do objeto. O método retorna o próprio objeto, permitindo incorporar apply em cadeias ou usá-lo em inicializadores.
data class ServerConfig(
var host: String = "localhost",
var port: Int = 8080,
var useTls: Boolean = false
)
val config = ServerConfig().apply {
host = "api.example.com"
port = 443
useTls = true
}
No exemplo apply configura ServerConfig acessando as propriedades diretamente via this (this é omitido). Sem apply, seria necessário escrever config.host = ..., config.port = ... — três referências repetitivas à mesma variável. O retorno do objeto permite salvar o resultado da configuração em val config. also, ao contrário de apply, passa o objeto via it e é adequado para efeitos colaterais.
fun saveUser(user: User) {
validate(user).also { result ->
println("Resultado da validação: $result")
}
val savedUser = user.also {
log("Saving user ${it.id}")
database.save(it)
}
}
No exemplo also é usado para registro — enquanto retorna o objeto original, realiza um efeito colateral (exibir uma mensagem) sem modificar o objeto. Isso é uma cadeia: validate(user) retorna User, also registra o resultado e passa o usuário adiante. also também é útil para adicionar elementos a coleções ou depurar no meio de uma cadeia de transformações.
let é uma scope function que retorna o resultado da lambda e passa o contexto via it. É a função principal para transformações seguras com null: se o objeto for nullable, a combinação ?.let { } executará o bloco apenas para valores não nulos. let também é usado para limitar o escopo de variáveis temporárias e para transformações do tipo map de um tipo para outro.
fun findUser(id: Int): User?
val displayName = findUser(42)?.let { user ->
"${user.name} (${user.email})"
} ?: "Unknown user"
// let com transformação de coleção
val numbers = listOf("1", "2", "3")
val parsed = numbers.firstOrNull()?.let {
it.toIntOrNull()
} ?: 0
No primeiro exemplo let é chamado via ?. — o bloco executa apenas se findUser retornou não nulo. A lambda recebe user via it (pode ser renomeado para clareza) e retorna uma string formatada. Se o usuário não for encontrado, o operador Elvis ?: fornece um valor padrão. No segundo exemplo, let converte uma string em número com um possível null.
run é um análogo de let com acesso via this, retornando o resultado da lambda. run é conveniente para cálculos que exigem contexto do objeto mas retornam um novo valor. Chamar run() sem objeto (como run { ... }) cria um escopo temporário para variáveis. with é uma não-extensão com acesso via this, adequada para agrupar chamadas sem encadeamento.
// run como cálculo baseado em contexto
val transformed = listOf(1, 2, 3).run {
filter { it > 1 }.map { it * 10 }
}
println(transformed) // [20, 30]
// with para agrupar chamadas
val info = with(StringBuilder()) {
append("Name: ")
append("Alice")
toString()
}
No exemplo run em uma lista retorna o resultado de filtragem e mapeamento — a própria transformação, não a lista. with recebe StringBuilder como argumento (não via ponto) e internamente trabalha com this: append, append, toString. O resultado de with é a string info. A escolha entre run e with é frequentemente subjetiva, mas with é preferível quando o objeto é passado de outro lugar sem encadeamento.
with difere das outras scope functions por não ser uma função de extensão. Ela recebe o objeto como primeiro argumento e a lambda como segundo. Dentro da lambda, o objeto é acessível via this. with é conveniente para agrupar chamadas de métodos do objeto sem salvar o resultado ou quando é necessário “entrar” temporariamente em um contexto para ler várias propriedades.
val user = User("Alice", "alice@example.com", "Admin")
val summary = with(user) {
"User $name has role $role and email $email"
}
println(summary)
// with para inicialização de componentes UI
with(TextView(context)) {
text = "Hello"
textSize = 18.0f
setTextColor(Color.BLUE)
}
No primeiro exemplo with forma a string summary acessando as propriedades de user diretamente via this. Sem with, seria necessário escrever "User ${user.name} has role ${user.role}" — repetição de user. No segundo exemplo, with agrupa a configuração de TextView: todos os métodos e propriedades são chamados em um único objeto sem repetição. Desvantagem de with — impossibilidade de usá-lo em uma cadeia, pois não é uma extensão e não retorna o objeto de contexto.
Perguntas frequentes
Use apply — ele retorna o objeto e fornece acesso via this, permitindo definir propriedades sem repetir o nome do objeto. also é adequado se você precisar de um parâmetro it explícito para distinguir das propriedades do objeto.
let passa o contexto via it (parâmetro explícito), run — via this (receptor implícito). let é conveniente para verificações null e transformações, run — para cálculos com contexto. Ambas retornam o resultado da lambda.
with não é uma extensão — recebe o objeto como argumento. Use with para agrupar chamadas quando não for necessário encadeamento. apply é uma extensão que retorna o objeto, conveniente em inicializadores e padrões builder.
Sim, mas tome cuidado com this — this aninhado cria confusão. Use let ou also com nomes explícitos para contextos aninhados para evitar ambiguidade ao referenciar o objeto externo.
Todas as scope functions são inline, portanto não criam sobrecarga de classes anônimas. A diferença de desempenho entre elas é insignificante. A escolha deve ser baseada na legibilidade e convenções, não na velocidade.
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