Students Save 30%! Learn & create with unlimited courses & creative assets Students Save 30%! Save Now
Advertisement
  1. Code
  2. Kotlin
Code

Kotlin a Partir do Princípio: Mais Divertido com Funções

by
Difficulty:IntermediateLength:LongLanguages:
This post is part of a series called Kotlin From Scratch.
Kotlin From Scratch: Packages and Basic Functions
Kotlin From Scratch: Advanced Functions

Portuguese (Português) translation by Luis Drum (you can also view the original English article)

Kotlin é uma  linguagem de programação moderna que é compilada em bytecode Java. É gratuita e de código aberto e promete fazer a codificação para o Android ainda mais divertida.

No artigo anterior, você aprendeu sobre pacotes e funções básicas em Kotlin. Funções são o cerne do Kotlin, então neste post vamos olhá-las mais de perto. Nós exploraremos os seguintes tipos de funções em Kotlin:

  • funções de nível superior
  • expressões lambda ou literais de função
  • funções anônimas
  • funções aninhadas ou locais
  •  funções infixas
  • funções membro

Você ficará surpreso com todas as coisas legais que você pode fazer com funções em Kotlin!

1. Funções de Nível Superior

Funções de nível superior são funções dentro de um pacote Kotlin que estão definidas fora de qualquer classe, objeto ou interface. Isto significa que elas são funções que você chama diretamente, sem a necessidade de criar qualquer objeto ou chamar qualquer classe.

Se você é um programador Java, você sabe que nós normalmente criamos métodos estáticos utilitários dentro de classes auxiliares. Essas classes auxiliares realmente não fazem nada — não têm qualquer estado ou métodos de instância, eles só agem como um contêiner para os métodos estáticos. Um exemplo típico é a classe Collections no pacote java.util e seus métodos estáticos.

Funções de nível superior em Kotlin podem ser usadas como um substituto para os métodos utilitários estáticos dentro de classes auxiliares que codificamos em Java. Vejamos como definir uma função de nível superior em Kotlin.

No código acima, definimos o pacote com.chikekotlin.projectx.utils dentro de um arquivo chamado UserUtils.kt e também definimos uma função utilitária de nível superior chamada checkUserStatus() dentro deste mesmo pacote e arquivo. Por uma questão de brevidade, esta função simples retorna a sequência de caracteres "online".

A próxima coisa que vamos fazer é usar essa função utilitária em outro arquivo ou pacote.

No código anterior, nós importamos a função em outro pacote e então a executamos! Como você pode ver, não precisamos criar um objeto ou referenciar uma classe para chamar essa função.

Interoperabilidade Java

Dado que o Java não suporta funções de nível superior, o compilador de Kotlin nos bastidores irá criar uma classe Java, e as funções individuais de nível superior serão convertidas em métodos estáticos. No nosso caso, a classe Java gerada foi UserUtilsKt com um método estático checkUserStatus().

Isto significa que os callers Java podem simplesmente chamar o método referenciando sua classe gerada, assim como para qualquer outro método estático.

Note que podemos mudar o nome da classe Java que o compilador Kotlin gera usando a anotação @JvmName.

No código acima, aplicamos a anotação de @JvmName e especificamos o nome de classe UserUtils para o arquivo gerado. Note também que esta anotação é colocada no início do arquivo de Kotlin, antes da definição de pacote.

Pode ser referenciado a partir de Java assim:

2. Expressões Lambda 

As expressões lambda (ou literais de função) também não estão vinculadas a qualquer entidade como uma classe, interface ou objeto. Elas podem ser passadas como argumentos para outras funções chamadas funções de ordem superior (discutiremos mais no próximo post). Uma expressão lambda representa apenas o bloco de uma função, e usá-las reduz a ilegibilidade em nosso código.

Se você é um programador Java, você sabe que o Java 8 e posteriores fornecem suporte para expressões lambda. Para usar expressões lambda em um projeto que oferece suporte a versões anteriores de Java como Java 5, 6 ou 7, usaremos a popular biblioteca Retrolambda.

Uma das coisas incríveis sobre Kotlin é que expressões lambda  são suportadas de maneira incomum. Por lambda não ser suportado em Java 6 ou 7, para o Kotlin interoperar com ele, Kotlin cria uma classe anônima de Java nos bastidores. Mas note que a criação de uma expressão lambda em Kotlin é completamente diferente do que é em Java.

Aqui estão as características de uma expressão lambda em Kotlin:

  • Ela deve estar entre chaves {}.
  • Ela  não deve ter a palavra-chave fun.
  • Não há nenhum modificador de acesso (privado, público ou protegido) porque não pertence a nenhuma classe, objeto ou interface.
  • Não tem nenhum nome de função. Em outras palavras, é anônima.
  • Nenhum tipo de retorno é especificado porque ele será inferido pelo compilador.
  • Parâmetros não estão cercados por parênteses ().

E, além do mais, podemos atribuir uma expressão lambda a uma variável e em seguida executá-la.

Criar Expressões Lambda

Vamos agora ver alguns exemplos de expressões lambda. No código abaixo, criamos um expressão lambda sem parâmetros e atribuímos a ela a variável message. Em seguida executamos a expressão lambda chamando message().

Vamos também ver como incluir parâmetros em uma expressão lambda.

No código acima, criamos uma expressão lambda com o parâmetro myString, juntamente com o parâmetro de tipo String. Como você pode ver, em frente ao tipo de parâmetro, há uma seta: refere-se ao corpo do lambda. Em outras palavras, esta seta separa o corpo do lambda da lista de parâmetros. Para torná-la mais concisa, podemos ignorar completamente o tipo de parâmetro (já inferido pelo compilador).

Para ter vários parâmetros, nós apenas os separamos com uma vírgula. E lembre-se, nós não envolvemos a lista de parâmetros com parênteses, como no Java.

No entanto, observe que, se os tipos de parâmetro não podem ser inferidos, eles devem ser especificados explicitamente (como neste exemplo), caso contrário o código não compila.

Passando Lambdas para Funções

Podemos passar as expressões lambda como parâmetros de funções: estas são chamadas de "funções de ordem superior", porque elas são funções de funções. Esses tipos de funções podem aceitar um lambda ou uma função anônima como parâmetro: por exemplo, a função de coleção last().

No código abaixo, passamos uma expressão lambda para a função last(). (Se você quer uma revisão em coleções Kotlin, visite o terceiro tutorial nesta série) Como o nome diz, ele retorna o último elemento na lista. last() aceita uma expressão lambda como parâmetro, e esta expressão, por sua vez, recebe um argumento do tipo String. Seu corpo de função serve como um atributo para pesquisar dentro de um subconjunto de elementos na coleção. Isso significa que a expressão lambda vai decidir quais os elementos da coleção serão considerados ao procurar pelo último.

Vamos ver como fazer a última linha do código acima mais legível.

O compilador de Kotlin nos permite remover os parênteses da função se o último argumento da função é uma expressão lambda. Como se pode observar no código acima, nos é permitido fazer isso porque o último e único argumento passado para a função last() é uma expressão lambda.

Além disso, podemos torná-la mais concisa, removendo o tipo de parâmetro.

Não precisamos especificar o tipo de parâmetro explicitamente, porque o tipo de parâmetro é sempre o mesmo que o tipo de elemento da coleção. No código acima, estamos chamando last em uma coleção de lista de objetos String, portanto, o compilador Kotlin é suficientemente esperto para saber que o parâmetro será também de um tipo String.

O Nome de Argumento it

Podemos  simplificar a expressão lambda ainda mais, substituindo o argumento da expressão lambda pelo argumento padrão gerado automaticamente, denominado it.

O nome de argumento it foi gerado automaticamente porque last pode aceitar uma expressão lambda ou uma função anônima (vamos chegar a isso em breve) com apenas um argumento, e seu tipo pode ser inferido pelo compilador.

Retorno Local em Expressões Lambda

Vamos começar com um exemplo. No código abaixo, passamos uma expressão lambda para a função foreach() invocada na coleção intList. Essa função irá percorrer a coleção e executar o lambda em cada elemento da lista. Se qualquer elemento for divisível por 2, ele irá parar e retornar do lambda.

Executar o código acima pode não ter lhe dado o resultado esperado. Isso ocorre porque essa instrução de retorno não retornará do lambda, mas da função surroundingFunction()! Isto significa que a última instrução do código em surroundingFunction() não vai executar.

Para corrigir esse problema, precisamos informar explicitamente de qual função retornar usando um rótulo ou uma tag de nome.

No código atualizado acima, especificamos a tag padrão @forEach imediatamente após o palavra-chave return dentro do lambda. Nós agora instruimos o compilador para retornar a partir do lambda em vez da função surroundingFunction() que o contém. Agora a última instrução de surroundingFunction() será executada.

Note que podemos também definir nosso próprio rótulo ou nome de tag.

No código acima, nós definido nosso rótulo personalizado chamado myLabel@ e então especificamos it para a palavra-chave return. O rótulo de @forEach gerado pelo compilador para a função forEach não está mais disponível porque definimos o nosso próprio.

No entanto, você logo verá como esse problema de retorno local pode ser resolvido sem rótulos quando discutirmos em breve as funções anônimas em Kotlin.

3. Funções Membro

Esse tipo de função é definido dentro de uma classe, interface ou objeto. Usar funções membro nos ajuda a modularizar nossos programas ainda mais. Vamos agora ver como criar uma função membro.

Este trecho de código mostra a classe Circle (discutiremos classes Kotlin em posts posteriores) que tem uma função membro calculateArea(). Essa função usa o parâmetro radius para calcular a área de um círculo.

Para chamar uma função membro, nós usamos o nome da classe que a contém ou a instância do objeto com um ponto, seguido do nome da função, passando quaisquer argumentos, se necessário.

4. Funções Anônimas

Uma função anônima é outra maneira de definir um bloco de código que pode ser passado para uma função. Ele não está vinculado a qualquer identificador. Aqui estão as características de uma função anônima em Kotlin:

  • não tem nome
  • é criada com a palavra-chave fun
  • contém o corpo de uma função

Porque nós passamos um lambda para a função last() acima, não podemos ser explícitos sobre o tipo de retorno. Para sermos explícitos sobre o tipo de retorno, precisamos usar uma função anônima em vez disso.

No código acima, substituímos a expressão lambda com uma função anônima, porque queremos ser explícitos sobre o tipo de retorno.

No final da seção lambda neste tutorial, usamos um rótulo para especificar de qual função retornar. Usar uma função anônima em vez de um lambda dentro da função forEach() resolve esse problema de forma mais simples. A expressão de retorno retorna da função anônima e não da circundante, que no nosso caso é surroundingFunction().

5. Funções Locais ou Aninhadas

Para levar ainda mais longe a modularização do programa, Kotlin nos fornece funções locais — também conhecidas como funções aninhadas. Uma função local é uma função que é declarada dentro de outra função.

Como se pode observar no trecho de código acima, temos duas funções de linha única: calCircumference() e calArea() aninhadas dentro da função printCircumferenceAndAread(). As funções aninhadas podem ser chamadas somente de dentro da função circundante e não fora. Novamente, o uso de funções aninhadas torna nosso programa mais modular e ordenado.

Podemos fazer nossas funções locais mais concisas, não  passando parâmetros explicitamente para elas. Isso é possível porque as funções locais têm acesso a todos os parâmetros e variáveis da função circundante. Vamos ver isso agora em ação:

Como você pode ver, esse código atualizado parece mais legível e reduz o ruído que tínhamos antes. Embora a função circundante neste exemplo dado seja pequena, em uma função circundante maior que pode ser dividida em pequenas funções aninhadas, esse recurso pode realmente vir a calhar.

6. Funções Infixas

A notação infix nos permite facilmente chamar uma função membro de um argumento ou uma função de extensão. Além de uma função ser de um argumento, você também deve definir a função usando o modificador infix. Para criar uma função infixa, dois parâmetros estão envolvidos. O primeiro parâmetro é o objeto alvo, enquanto o segundo parâmetro é apenas um único parâmetro passado para a função.

Criando uma Função Membro Infixa

Vamos ver como criar uma função infixa em uma classe. No exemplo de código abaixo, criamos uma classe Student com um campo de instância mutável kotlinScore . Criamos uma função infixa usando o modificador infix antes da palavra-chave fun. Como você pode ver abaixo, criamos uma função infixa addKotlinScore() que recebe uma pontuação e adiciona ao campo de instância kotlinScore.

Chamando uma Função Infixa

Vamos ver também como invocar a função  infixa que criamos. Para chamar uma função infixa em Kotlin, não precisamos usar a notação de ponto, e não temos que envolver o parâmetro com parênteses.

No código acima, chamamos a função infixa, o objeto alvo é student e o double 95.00 é o parâmetro passado para a função.

Usar funções infixas sensatamente pode tornar nosso código mais expressivo e mais claro do que o estilo normal. Isto é muito apreciado quando se escreve testes de unidade em Kotlin (discutiremos testes em Kotlin em um post futuro).

A Função Infixa to

Em Kotlin, podemos fazer a criação de uma instância de Pair mais sucinta, usando a função infixa to, em vez do construtor Pair. (Nos bastidores to também cria uma instância de Pair.) Note  que a função to também é uma função de extensão (discutiremos mais no próximo post).

Vamos agora comparar a criação de uma instância de Pair  usando a função infixa to e diretamente usando o construtor de Pair, que executa a mesma operação e ver qual é melhor.

Como você pode ver no código acima, usar  a função infixa to é mais conciso do que usar o construtor de Pair para criar uma instância de Pair. Lembre-se que usando a função infixa to, 234 é o objeto de alvo e a String "Nigeria" é o parâmetro passado para a função. Além disso, note que também podemos fazer isso para criar um tipo Pair:

No post Intervalos e Coleções, criamos uma coleção de mapas em Kotlin, dando-lhe uma lista de pares — o primeiro item, sendo a chave e o segundo o valor. Vamos também comparar a criação de um mapa usando tanto a função infixa to como o construtor Pair para criar os pares individuais.

No código acima, criamos uma lista separada por vírgulas de tipos de Pair usando a função infixa to e passamos para a função mapOf(). Também criamos o mesmo mapa diretamente usando o construtor de Pair para cada par.

Como você pode ver mais uma vez, manter a função infixa to tem menos ruído do que usar o construtor de Pair.

Conclusão

Neste tutorial, você aprendeu sobre algumas das coisas legais que você pode fazer com funções em Kotlin. Nós cobrimos:

  • funções de nível superior
  • expressões lambda ou funções literais
  • funções membro
  • funções anônimas
  • funções aninhadas ou locais
  • funções infixas

Mas isso não é tudo! Ainda há mais para aprender sobre funções em Kotlin. Então no próximo post, você vai aprender alguns usos avançados de funções, tais como funções de extensão, funções de ordem superior e closures. Te vejo em breve!

Para saber mais sobre a linguagem Kotlin, eu recomendo visitar a documentação Kotlin. Ou confira alguns dos nossos outros post de desenvolvimento de Android apps aqui no Envato Tuts+!

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.