Olá pessoal, hoje continuando falando sobre os recursos da linguagem Kotlin, vamos falar sobre o uso de funções infix.
O que é o Infix
Ao utilizar algumas bibliotecas famosas do mundo Kotlin nos deparamos com códigos sendo utilizados da seguinte forma:
Uso do Assertion da biblioteca Kotest. |
No exemplo acima, temos uma expressão diferente, que é a expect shouldBe "Kotlin", aqui temos o uso de uma função infix, que basicamente irá testar se a variável expect possui o valor "Kotlin",
Ao olhar para esta expressão sem conhecer o infix, não percebemos que na verdade o shouldBe é uma função da variável expect, e recebe como argumento a String Kotlin, abaixo temos o mesmo efeito do código mostrado anteriormente:
Uso do Assertion da biblioteca Kotest sem o uso de Infix. |
Esta abordagem de fluência na chamada é muito comum quando usamos DSL (Domain, Specification Language) ou Linguagem de Domínio Especifico, para a criação de DSL usando Kotlin saber funções infix é fundamental.
Criando uma Função Infix
Para criar uma função infix, antes precisamos entender as regras para poder utilizar este recurso, que são eles:
- Pode ser utilizada em Extension Functions;
- Pode ser utilizada em Funções membros;
- A função deve ser declarada com um único parâmetro;
- O parâmetro da função não podem ter valor padrão;
- O parâmetro da função não pode ser um vararg.
Vamos agora ver como utilizar o infix em conjunto com as extension functions:
data class Computer(val itens:List<String>) infix fun Computer.hasItem (item:String) = this.itens.contains(item) fun main() { val computer = Computer(listOf("Teclado", "Mouse", "Monitor")) //Chamada Tradicional val result = computer.hasItem("Teclado") //Chamada usando o recurso do modificador infix val resultInfix = computer hasItem "Teclado" println(result) println(resultInfix) }
Neste exemplo temos a classe Computer, com uma propriedade chamada itens do tipo List<String>, logo após, temos a declaração de uma extension function chamada hasItem, e perceba que ela possui o modificador infix em sua declaração, esta apenas verifica se a propriedade itens possui a String encaminhada como argumento.
Função hasItem declarada com o modificador infix. |
Dentro do método main realizamos a chamada de uma função infix de 2 formas diferentes, vale lembrar que mesmo a função sendo declarada como infix, ela ainda pode ser executada da forma tradicional.
Chamada da função infix hasItem. |
De primeira vista, parece algo que não agrega muito para os códigos do dia a dia, mas ao escrever DSLs ou Libraries, você nota que o ganho é alto, tanto na escrita reduzida de código, como também no entendimento das regras de negocio.
Função membro com Infix
Agora no segundo exemplo iremos usar o infix em uma função membro:
data class Car(val name:String){ infix fun nameStartsWith(letter:Char) = this.name.startsWith(letter) } fun main() { val car = Car("Azera") //Chamada Tradicional val result = car.nameStartsWith('B') //Chamada usando o recurso do modificador infix val resultInfix = car nameStartsWith 'A' println(result) println(resultInfix) }
Neste exemplo criamos uma classe Car com uma propriedade name, dentro da classe Car também declaramos a função infix chamada nameStartsWith, o objetivo é verificar se o nome do Carro começa com uma determinada letra.
A declaração com relação a extension function não muda, a única diferença é que a função infix nameStartsWith pertence a classe Car.
Chamada da função infix nameStartsWith. |
Nestes exemplos, podemos perceber o potencial que temos com o uso de funções infix, agora vamos ver um exemplo mais complexo para termos uma visão ampla de como este recurso pode nos auxiliar no dia a dia.
Chamadas encadeadas com Infix
Além das chamadas que vimos anteriormente, podemos realizar chamadas encadeadas que usam infix, este recurso é um ponta pé inicial para entrar no mundo das DSLs, vamos analisar o exemplo abaixo:
data class Product(val name:String, val category:Category) data class Category(val name:String){ infix fun product(name:String) = Product(name, this) } class Store{ infix fun addCategory(name:String) = Category(name) } fun main(){ val store = Store() val productInfix = store addCategory "Eletrônicos" product "TV" println("productInfix = $productInfix") val productWithoutInfix = store.addCategory("Eletrônicos").product("TV") println("productWithoutInfix = $productWithoutInfix")}
No exemplo criamos 3 classes, onde a classe Store possui uma função infix chamada addCategory responsável por criar uma instância da classe Category, na classe Category temos uma função chamada product, esta recebe uma String como argumento e cria uma instância da classe Product.
O uso das funções infix acontece da seguinte forma:
Criação de um Product com chamadas infix em cascata. |
Ao analisar esta expressão, tudo parece uma simples frase dizendo como queremos montar nosso produto, vamos analisar a chamada convencional:
Criação de um Product sem o uso da chamada infix. |
O recurso de chamadas infix ajuda em vários aspectos, sendo os principais:
- Criação de modelos de domínio complexos;
- Criação de funções helpers;
- Criação de DSLs;
- Criação de Libraries ou Frameworks.
Um ponto que vale lembrar é: USE COM CAUTELA, porque apesar de ser um recurso interessante, seu uso deliberado pode deixar o código mais complexo, principalmente para novos integrantes de uma equipe ou para desenvolvedores que não estejam familiares com este recurso.
Comentários
Postar um comentário