Introdução ao Java Funcional
1. Visão geral
Neste tutorial, forneceremos uma visão geral rápida da bibliotecaFunctional Java junto com alguns exemplos.
2. A Biblioteca Java Funcional
A biblioteca Java funcional é uma biblioteca de código aberto destinada a facilitar a programação funcional em Java. A biblioteca fornece muitas abstrações de programação básicas e avançadas comumente usadas emFunctional Programming.
Grande parte da funcionalidade da biblioteca gira em torno da interfaceF. This F interface models a function that takes an input of type A and returns an output of type B. Tudo isso é construído em cima do próprio sistema de tipos do Java.
3. Dependências do Maven
Primeiro, precisamos adicionar odependencies necessário ao nosso arquivopom.xml:
org.functionaljava
functionaljava
4.8.1
org.functionaljava
functionaljava-java8
4.8.1
org.functionaljava
functionaljava-quickcheck
4.8.1
org.functionaljava
functionaljava-java-core
4.8.1
4. Definindo uma Função
Vamos começar criando uma função que podemos usar em nossos exemplos mais tarde.
Sem o Java funcional, um método básico de multiplicação seria algo como:
public static final Integer timesTwoRegular(Integer i) {
return i * 2;
}
Usando a biblioteca Java Funcional, podemos definir essa funcionalidade de maneira um pouco mais elegante:
public static final F timesTwo = i -> i * 2;
Acima, vemos um exemplo da interfaceF que leva umInteger como entrada e retorna esseInteger vezes dois como sua saída.
Aqui está outro exemplo de uma função básica que leva umInteger como entrada, mas, neste caso, retorna umBoolean para indicar se a entrada era par ou ímpar:
public static final F isEven = i -> i % 2 == 0;
5. Aplicando uma Função
Agora que temos nossas funções no lugar, vamos aplicá-las a um conjunto de dados.
A biblioteca Java funcional fornece o conjunto usual de tipos para gerenciar dados como listas, conjuntos, matrizes e mapas. The key thing to realize is that these data types are immutable.
Além disso, a biblioteca fornececonvenience functions to convert to and from standard Java Collections classes, se necessário.
No exemplo abaixo, vamos definir uma lista de inteiros e aplicar nossa funçãotimesTwo a ela. Também chamaremosmap usando uma definição embutida da mesma função. Obviamente, esperamos que os resultados sejam os mesmos:
public void multiplyNumbers_givenIntList_returnTrue() {
List fList = List.list(1, 2, 3, 4);
List fList1 = fList.map(timesTwo);
List fList2 = fList.map(i -> i * 2);
assertTrue(fList1.equals(fList2));
}
Como podemos ver,map retorna uma lista do mesmo tamanho, onde o valor de cada elemento é o valor da lista de entrada com a função aplicada. A lista de entrada em si não muda.
Aqui está um exemplo semelhante usando nossa funçãoisEven:
public void calculateEvenNumbers_givenIntList_returnTrue() {
List fList = List.list(3, 4, 5, 6);
List evenList = fList.map(isEven);
List evenListTrueResult = List.list(false, true, false, true);
assertTrue(evenList.equals(evenListTrueResult));
}
Since the map method returns a list, we can apply another function to its output. A ordem em que invocamos nossas funçõesmap altera nossa saída resultante:
public void applyMultipleFunctions_givenIntList_returnFalse() {
List fList = List.list(1, 2, 3, 4);
List fList1 = fList.map(timesTwo).map(plusOne);
List fList2 = fList.map(plusOne).map(timesTwo);
assertFalse(fList1.equals(fList2));
}
A saída das listas acima será:
List(3,5,7,9)
List(4,6,8,10)
6. Filtrando usando uma função
Outra operação freqüentemente usada em Programação Funcional étake an input and filter out data based on some criteria. E como você provavelmente já adivinhou, esses critérios de filtragem são fornecidos na forma de uma função. Essa função precisará retornar um booleano para indicar se os dados precisam ou não ser incluídos na saída.
Agora, vamos usar nossa funçãoisEven para filtrar os números ímpares de uma matriz de entrada usando o métodofilter:
public void filterList_givenIntList_returnResult() {
Array array = Array.array(3, 4, 5, 6);
Array filteredArray = array.filter(isEven);
Array result = Array.array(4, 6);
assertTrue(filteredArray.equals(result));
}
Uma observação interessante é que, neste exemplo, usamos umArray em vez de umList como usamos nos exemplos anteriores, e nossa função funcionou bem. Because of the way functions are abstracted and executed, they do not need to be aware of what method was used to collect the input and output.
Neste exemplo, também usamos nossa própria funçãoisEven, mas a própria classeInteger do Functional Java também tem funções padrão parabasic numerical comparisons.
7. Aplicação da lógica booleana usando uma função
Na Programação Funcional, freqüentemente usamos lógica como “somente faça isso se todos os elementos satisfizerem alguma condição” ou “somente faça isso se pelo menos um elemento atender a alguma condição”.
A biblioteca Functional Java nos fornece atalhos para essa lógica por meio dos métodosexistseforall:
public void checkForLowerCase_givenStringArray_returnResult() {
Array array = Array.array("Welcome", "To", "example");
assertTrue(array.exists(s -> List.fromString(s).forall(Characters.isLowerCase)));
Array array2 = Array.array("Welcome", "To", "example");
assertFalse(array2.exists(s -> List.fromString(s).forall(Characters.isLowerCase)));
assertFalse(array.forall(s -> List.fromString(s).forall(Characters.isLowerCase)));
}
No exemplo acima, usamos uma matriz de seqüências de caracteres como nossa entrada. Chamar a funçãofromString converterá cada uma das strings da matriz em uma lista de caracteres. Para cada uma dessas listas, aplicamosforall(Characters.isLowerCase).
Como você provavelmente adivinhou,Characters.isLowerCase é uma função que retorna verdadeiro se um caractere estiver em minúsculas. Portanto, aplicarforall(Characters.isLowerCase) a uma lista de caracteres retornará apenastrue se a lista inteira consistir em caracteres minúsculos, o que por sua vez indica que a string original estava toda em minúsculas.
Nos primeiros dois testes, usamosexists porque queríamos apenas saber se pelo menos uma string estava em minúsculas. O terceiro teste usouforall para verificar se todas as strings estavam em minúsculas.
8. Lidando com valores opcionais com uma função
Manipular valores opcionais no código normalmente requer verificações de== null ouisNotBlank. Java 8 agora fornece a classeOptional para lidar com essas verificações de maneira mais elegante, e a biblioteca Functional Java oferece uma construção semelhante para lidar com dados ausentes de maneira elegante por meio de sua classeOption:
public void checkOptions_givenOptions_returnResult() {
Option n1 = Option.some(1);
Option n2 = Option.some(2);
Option n3 = Option.none();
F> function = i -> i % 2 == 0 ? Option.some(i + 100) : Option.none();
Option result1 = n1.bind(function);
Option result2 = n2.bind(function);
Option result3 = n3.bind(function);
assertEquals(Option.none(), result1);
assertEquals(Option.some(102), result2);
assertEquals(Option.none(), result3);
}
9. Reduzindo um conjunto usando uma função
Finalmente, veremos a funcionalidade para reduzir um conjunto. "Reduzir um conjunto" é uma maneira elegante de dizer "agregando um valor".
The Functional Java library refers to this functionality as folding.
Uma função precisa ser especificada para indicar o que significa dobrar o elemento. Um exemplo disso é a funçãoIntegers.add para mostrar que os inteiros em uma matriz ou lista precisam ser adicionados.
Com base no que a função faz ao dobrar, o resultado pode ser diferente dependendo se você começa a dobrar da direita ou da esquerda. É por isso que a biblioteca Functional Java fornece as duas versões:
public void foldLeft_givenArray_returnResult() {
Array intArray = Array.array(17, 44, 67, 2, 22, 80, 1, 27);
int sumAll = intArray.foldLeft(Integers.add, 0);
assertEquals(260, sumAll);
int sumEven = intArray.filter(isEven).foldLeft(Integers.add, 0);
assertEquals(148, sumEven);
}
O primeirofoldLeft simplesmente adiciona todos os inteiros. Enquanto o segundo primeiro aplica um filtro e depois adiciona os números inteiros restantes.
10. Conclusão
Este artigo é apenas uma breve introdução à biblioteca Java Funcional.
Como sempre, o código-fonte completo do artigo está disponívelover on GitHub.