DEV Community

Cover image for Usando funções de escopo no Kotlin
Alex Felipe
Alex Felipe

Posted on

Usando funções de escopo no Kotlin

Se tem uma funcionalidade no Kotlin que é usada com muita frequência, mas muitas pessoas não tem ideia do que seja, são as funções de escopo.

fun main() {
    val user: User? = findUserById(1)
    user?.let {
        println(it)
    }
}
Enter fullscreen mode Exit fullscreen mode

Conseguiu identificar a função de escopo? Não? Não se preocupe que vamos entendê-las!

Conhecendo as funções de escopo

Basicamente, função de escopo é uma função que executa um bloco de código dentro de um contexto de um objeto! Definição bem abstrata, né? Então vamos para a prática:

user.let {
    ...
}
Enter fullscreen mode Exit fullscreen mode

Neste código, let é uma função de escopo que executa no contexto do user que é do tipo User?. Dentro deste contexto, temos acesso a user a partir do parâmetro da lambda, podemos até mesmo mudar o nome:

user.let { u ->
    println(u)
}
Enter fullscreen mode Exit fullscreen mode

Usar funções de escopo dessa forma, não tem diferença de apenas chamar println(user), concorda? Então porque elas existem?

Safe call com funções de escopo

Uma das maiores utilidades de funções de escopo é combinar com a safe call:

user?.let {
    println(it.name)
}
Enter fullscreen mode Exit fullscreen mode

Todo parâmetro único em expressões lambdas são subentendidos como it por padrão.

Essa abordagem tem o mesmo resultado de usar um if que garante que não é nulo. A diferença fica na sintax que é mais sucinta, permitindo até mesmo usar o it ou um nome que identifica melhor o contexto, como por exemplo, nonNullUser:

user?.let { nonNullUser ->
    println(nonNullUser.name)
}
Enter fullscreen mode Exit fullscreen mode

Variáveis apenas dentro do escopo esperado

Um outro caso que auxilia bastante, é que podemos trabalhar com variáveis que irão existir apenas nas funções de escopo. Se pegarmos a amostra inicial como exemplo, podemos criar a seguinte variação:

findUserById(1)?.let { user ->
    println(user.name)
}
Enter fullscreen mode Exit fullscreen mode

Notou a diferença? Com essa abordagem, a existência da variável user é só dentro de let! Logo, você não precisa mais se preocupar com nullable ou criar uma variável desnecessária no resto da aplicação.

Retornos em funções de escopo

Outro detalhe interessante, as funções de escopo podem retornar valores diferentes! Se usarmos o let como exemplo, ele sempre devolve o valor da última instrução, logo, se println() for a última instrução, será retornado Unit:

val result: Unit? = findUserById(1)?.let { user ->
    println(user.name)
}
Enter fullscreen mode Exit fullscreen mode

Nesse caso em específico é Unit? por conta da safe call que sempre retorna um nullable! Caso modificarmos a última instrução, teremos um nullable diferente:

val result: Long? = findUserById(1)?.let { user ->
    println(user.name)
    user.id
}
Enter fullscreen mode Exit fullscreen mode

Portanto, se atente aos retornos do let e de outras funções de escopo!

Função de escopo para configurar objetos durante a criação

Uma função de escopo bastante usada por conta do seu retorno, é a apply. Um dos principais motivos é que ela sempre retorna o objeto do contexto.

Isso pode ser um pouco confuso, mas tem seus casos de uso. No artigo de datas do Jetpack Compose, eu fiz a implementação de um formatador de datas utilizando a apply:

fun Long.toBrazilianDateFormat(
    pattern: String = "dd/MM/yyyy"
): String {
    val date = Date(this)
    val formatter = SimpleDateFormat(
        pattern, Locale("pt-br")
    ).apply {
        timeZone = TimeZone.getTimeZone("GMT")
    }
    return formatter.format(date)
}
Enter fullscreen mode Exit fullscreen mode

Veja que ao criar o SimpleDateFormat, é feita uma configuração de TimeZone e o retorno é o próprio formatador criado!

Em outras palavras, o apply serve para aplicar configurações extras após criação de um objeto. Logo, a variável retornada será criada com as configurações esperadas!

Inclusive, essa mesma implementação poderia ser mais sucinta e sem nenhuma variável:

fun Long.toBrazilianDateFormat(
    pattern: String = "dd/MM/yyyy"
): String = SimpleDateFormat(pattern, Locale("pt-br"))
    .apply {
        timeZone = TimeZone.getTimeZone("GMT")
    }.format(Date(this))
Enter fullscreen mode Exit fullscreen mode

Para saber mais

Esses são os casos mais comuns que utilizo funções de escopo no Kotlin, mas não para por aqui! Caso você quiser conhecer as demais funções como run, also, with etc, recomendo que veja a documentação sobre funções de escopo.

Também, você pode aplicar outras técnicas, como chamadas encadeadas que permite usar mais de uma função de escopo em uma única chamada.

E ai, o que achou das funções de escopo do Kotlin? Você também usa no seu código? Aplica outras técnicas? Me conte aqui nos comentários 😄

Top comments (0)