Programação Avançada Kotlin Parte 2

Tempo de leitura: 4 minutes

Se você é novo no Kotlin, verifique minhas postagens anteriores Guia de Kotlin para iniciantes e Programação avançada do Kotlin e siga em frente para um melhor entendimento. Nesta postagem, vamos verificar alguns conceitos avançados relacionados à programação funcional do Kotlin.

 

Funções de retorno de unidade

No Kotlin, cada função tem um tipo de função. Se a função não retornar nada, o tipo de retorno da função será Unidade. É opcional especificar o tipo de retorno Unit explicitamente. É semelhante ao void no caso do Java.

fun doSomething(name: String?): Unit {
    ....
    // `return Unit` ou `return` é opcional
}

ou 

fun doSomething(name: String?) {}

 

Funções com Varargs

Uma função que aceita um número variável de argumentos (varargs).

fun <T> listConversion(vararg ts: T): List<T> {
    val result = ArrayList<T>()
    for (t in ts) 
        result.add(t)
    return result
}

 

A função acima pode ser chamada passando n número de parâmetros do tipo T

val list = listConversion("abc","xyz","pqr")

 

Notação Infix

infix é uma palavra-chave em Kotlin. Podemos usar essa palavra-chave infixa com funções para que possamos chamar essas funções no objeto de classe sem usar o ponto “.” e parênteses (). Isso é chamado de notação de infixo (omitindo o ponto e os parênteses para a chamada de função). notações infixas ajudam a aumentar a legibilidade do programa. Vamos dar uma olhada em um exemplo simples para uma melhor compreensão

infix fun Int.multiply(x : Int) : Int = this * x
val m = 10.multiply(20)
val n = 10 multiply 20       // chamada de notação infixa

 

Para habilitar uma notação de infixo de função, precisamos adicionar uma palavra-chave infixo antes dela. Ao contrário de outras funções, existem três requisitos que devem ser atendidos para ser uma função infixo. Esses são:

  • Devem ser funções-membro ou funções de extensão;
  • Eles devem ter um único parâmetro;
  • O parâmetro não deve aceitar um número variável de argumentos (sem varargs) e não deve ter valor padrão.

“As chamadas de função Infix têm precedência mais baixa do que os operadores aritméticos, type casts e o operador rangeTo. Por outro lado, a precedência da chamada da função infixa é maior do que a dos operadores booleanos && e ||, is- e in-checks e alguns outros operadores. Confira mais detalhes na documentação do Kotlin

 

Quais são os tipos de função

No Kotlin, cada função tem um tipo de função. O Kotlin usa tipos de função para representar o tipo de função. O tipo de função é baseado em seus parâmetros e tipo de retorno. O tipo de função é indicado com uma lista de tipo de parâmetro entre parênteses e uma seta para o tipo de retorno. A sintaxe do tipo de função é

(parameter1Type, parameter2Type) -> returnType

No lado esquerdo contém os parâmetros entre parênteses e no lado direito tem o tipo de retorno, ambos estão ligados por uma seta

Vamos verificar alguns tipos de função:

  • () -> Unidade – O tipo de função que não leva parâmetros e não retorna nada
  • (Int, Int) -> Int— O tipo de função que leva dois parâmetros Int e retorna um valor Int
  • A. (B) -> C representa funções que podem ser chamadas em um objeto receptor de A com um parâmetro de B e retorna um valor de C.
  • () -> () -> () uma função que retorna outras funções que, por sua vez, não retorna nada.

“Podemos fazer referência à própria função prefixando seu nome com ::”

 

Literais de função

Expressões lambda e funções anônimas são “literais de função”, ou seja, funções que não são declaradas, mas passadas imediatamente como uma expressão. Vamos obter alguns insights sobre eles.

 

Expressão Lambda

Uma função sem nome é chamada de função anônima. As expressões lambda são semelhantes às funções anônimas que consistem em um bloco de instruções a serem executadas. Kotlin Lambdas são muito semelhantes aos Java Lambdas. A sintaxe de uma expressão lambda é

val lambdaName = { parameters -> codeBodyToBeExecuted }
                 ou
val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
                 ou 
val sum = { x: Int, y: Int -> x + y }

 

A expressão lambda é cercada por chaves {…} e o código que precisa ser executado e os parâmetros ficam dentro dessas chaves. Os tipos de parâmetros são opcionais se puderem ser inferidos. O corpo de Lambda vai atrás da seta ->. Vejamos um exemplo simples:

val add: (Int, Int) -> Int = {a , b -> a + b} 
add(2,3)

ou

val add = {a:Int , b:Int -> a + b}
add(2,3)

 

No primeiro caso, os tipos de a, b dentro de {} são inferidos do tipo especificado no lado esquerdo e vice-versa vale para um segundo. Se houver apenas um parâmetro, ele pode ser omitido junto com a seta -> e podemos usá-lo como referência para o parâmetro único. é o nome implícito de um único parâmetro, se não for especificado.

var incrementExpression: (Int) -> Int = { it + 1 }

A expressão final é o valor que será retornado após a execução de um lambda:

list.filter {
    val shouldFilter = it > 0 
    shouldFilter
}

Se o parâmetro lambda não estava sendo usado, podemos colocar um sublinhado em vez de qualquer nome:

map.forEach { _, mapValue-> println("$mapValue!") }

 

Funções anônimas

Uma função anônima é muito parecida com uma função normal, exceto que o nome da função não será especificado. O corpo de uma função anônima pode ser uma expressão ou um bloco. Não é nada além de uma função normal sem um nome.

fun(x: Int, y: Int): Int = x + y

       ou
       
fun(x: Int, y: Int): Int {
    return x + y
}

 

Invocar uma instância de tipo de função

Um valor de um tipo de função pode ser chamado usando seu operador invoke(..) f.invoke(x) ou apenas f(x).

val multiply: (Int, Int) -> Int = {x , y -> x * y} 
multiply(4,5)
multiply.invoke(4,5)

{m:Int, n:Int -> m * n}.invoke(4,5) 
{m:Int, n:Int -> m * n}(4,5) 

val f: Int.(Int) -> Int = { x -> this * x}
f.invoke(4,5) 
f(4,5) 
4.f(5)

O resultado de todas as equações é o mesmo, ou seja, 20.

 

Resumo

Agora você deve ter uma ideia básica da programação funcional em Kotlin.