Funções do escopo Kotlin

Funções do escopo Kotlin

1. Visão geral

As funções de escopo são muito úteis, e as usamos frequentemente no código Kotlin.

Neste tutorial, vamos explicar o que são e também fornecer alguns exemplos de quando usar cada um.

2. also

Primeiro, vamos dar uma olhada nas funções de mutaçãoalsoeapply.

Simplificando,a mutation function operates on the given object and returns it.

No caso dealso, um método de extensão, fornecemos um lambda que opera no objeto estendido:

inline fun T.also(block: (T) -> Unit): T

Ele retornará o objeto em que foi invocado, o que é útil quando queremos gerar alguma lógica secundária em uma cadeia de chamadas:

val headers = restClient
  .getResponse()
  .also { logger.info(it.toString()) }
  .getHeaders()

Note our use of it, pois isso se tornará importante mais tarde.

E podemos usaralso para inicializar objetos:

val aStudent = Student().also { it.name = "John" }

Claro, como podemos nos referir à instância comoit, também podemos renomeá-la,often creating something more readable:

val aStudent = Student().also { newStudent -> newStudent.name = "John"}

Certamente, se o lambda contiver uma lógica complexa, poder nomear a instância ajudará nossos leitores.

3. apply

Mas, parâmetro lambda demaybe we don’t want the extra verbosity of an it.

apply  é exatamente comoalso, mas com umthis implícito:

inline fun T.apply(block: T.() -> Unit): T

Podemos usarapply como fizemosalso para inicializar um objeto. Observe que não usamosit, embora:

val aStudent = Student().apply {
    studentId = "1234567"
    name = "Mary"
    surname = "Smith"
}

Ou, podemos usá-lo para criar facilmente objetos no estilo do construtor:

data class Teacher(var id: Int = 0, var name: String = "", var surname: String = "") {
    fun id(anId: Int): Teacher = apply { id = anId }
    fun name(aName: String): Teacher = apply { name = aName }
    fun surname(aSurname: String): Teacher = apply { surname = aSurname }
}

val teacher = Teacher()
  .id(1000)
  .name("Martha")
  .surname("Spector")

A principal diferença aqui é quealso usait, enquantoapply  não.

4. let

Agora, vamos dar uma olhada nas funções de transformaçãolet, run,ewith, que são apenas um passo mais complexo do que as funções de mutação.

Simplificando, uma função de transformação leva umsource de um tipo ereturns a target of another type.

Primeiro, élet:

inline fun  T.let(block: (T) -> R): R

É bem parecido comalso, exceto que nosso bloco retornaR em vez deUnit.

Vamos ver como isso faz a diferença.

Primeiro, podemos usarlet para converter de um tipo de objeto para outro,like taking a StringBuilder and computing its length:

val stringBuilder = StringBuilder()
val numberOfCharacters = stringBuilder.let {
    it.append("This is a transformation function.")
    it.append
      ("It takes a StringBuilder instance and returns the number of characters in the generated String")
    it.length
}

Ou então, podemos chamá-lo condicionalmente com o operador Elvis, fornecendo também um valor padrão:

val message: String? = "hello there!"
val charactersInMessage = message?.let {
    "value was not null: $it"
} ?: "value was null"

let é diferente dealso em que o tipo de retorno muda.

5. run

run está relacionado alet da mesma forma queapply está relacionado aalso:

inline fun  T.run(block: T.() -> R): R

Observe que retornamos um tipoR comolet, tornando esta uma função de transformação, mas pegamos umthis, implícito comoapply.

A diferença, embora sutil, se torna aparente com um exemplo:

val message = StringBuilder()
val numberOfCharacters = message.run {
    append("This is a transformation function.")
    append("It takes a StringBuilder instance and returns the number of characters in the generated String")
    length
}

Comlet,we referred to the message instance as it, but here, the message is the implicit this inside the lambda.

E podemos usar a mesma abordagem delet com nulidade:

val message: String? = "hello there!"
val charactersInMessage = message?.run {
    "value was not null: $this"
} ?: "value was null"

6. with

Nossa última função de transformação éwith. . É comorun porque tem umthis implícito, mas não é um método de extensão:

inline fun  with(receiver: T, block: T.() -> R): R

We can use with to restrict an object to a scope. Outra maneira de ver isso é agrupando logicamente várias chamadas para um determinado objeto:

with(bankAccount) {
    checkAuthorization(...)
    addPayee(...)
    makePayment(...)
}

7. Conclusão

Neste artigo, exploramos funções de escopo diferentes, categorizamos e explicamos em termos de seus resultados. Há alguma sobreposição em seu uso, mas com alguma prática e bom senso, podemos aprender qual função de escopo aplicar e quando.

Todos os exemplos podem ser encontrados emGitHub project.