1. Inovações em Java 8: programação funcional

Com o lançamento do Java 8, a linguagem ganhou um poderoso suporte para programação funcional . Você poderia até dizer que ganhou o tão esperado suporte para programação funcional. A codificação ficou mais rápida, embora o código fosse mais difícil de ler 🙂

Antes de aprender programação funcional em Java, recomendamos que você entenda bem três coisas:

  1. OOP, herança e interfaces ( Níveis 1-2 na missão Java Core ).
  2. Implementações de método padrão em uma interface .
  3. Classes internas e anônimas .

A boa notícia é que você não precisa saber tudo isso para usar muitos dos recursos da programação funcional em Java. A má notícia é que será difícil entender exatamente como tudo está organizado e como tudo funciona sem saber sobre classes internas anônimas.

Nas próximas lições, vamos nos concentrar em como é fácil e simples usar os recursos de programação funcional do Java, sem um conhecimento profundo de como ele funciona.

Leva meses para entender todas as nuances da programação funcional em Java. Você pode aprender a ler esse código em algumas horas. Portanto, sugerimos começar pequeno. Mesmo que seja com fluxos de E/S.


2. Fluxos de E/S: pipelines de fluxo

Você se lembra que uma vez você aprendeu sobre fluxos de I/O: InputStream, OutputStream, Reader, Writeretc.?

Havia classes de fluxo que liam dados de fontes de dados , como FileInputSteam, e havia fluxos de dados intermediários que liam dados de outros fluxos, como InputStreamReadere BufferedReader.

Esses fluxos podem ser organizados em pipelines de processamento de dados. Por exemplo, assim:

FileInputStream input = new FileInputStream("c:\\readme.txt");
InputStreamReader reader = new InputStreamReader(input);
BufferedReader buff = new BufferedReader(reader);

String text = buff.readLine();

É importante observar que nas primeiras linhas de código, estamos apenas construindo uma cadeia de Streamobjetos. Os dados ainda não passaram pelo pipeline.

Mas assim que chamarmos o buff.readLine()método, acontecerá o seguinte:

  1. O BufferedReaderobjeto chama o read()método no InputStreamReaderobjeto
  2. O InputStreamReaderobjeto chama o read()método no FileInputStreamobjeto
  3. O FileInputStreamobjeto começa a ler os dados do arquivo

Em outras palavras, não há movimento de dados ao longo do pipeline de fluxo até começarmos a chamar métodos como read()ou readLine(). A mera construção do pipeline de fluxo não conduz os dados através dele. Os próprios fluxos não armazenam dados. Eles só leem dos outros.

Coleções e streams

A partir do Java 8, tornou-se possível obter um fluxo para ler dados de coleções (e não apenas delas). Mas isso não é o mais interessante. Na verdade, tornou-se possível construir cadeias complexas de fluxos de dados de maneira fácil e simples. E ao fazer isso, o código que antes ocupava de 5 a 10 linhas agora pode ser escrito em 1 a 2 linhas.

Exemplo de localização da string mais longa em uma lista de strings:

Encontrando a string mais longa
ArrayList<String> list = new ArrayList<String>();
Collections.addAll(list, "Hello", "how's", "life?");
String max = list.stream().max((s1, s2)-> s1.length()-s2.length()).get();
ArrayList<String> list = new ArrayList<String>();
Collections.addAll(list, "Hello", "how's", "life?");
Stream<String> stream = list.stream();
Optional<String> optional = stream.max((s1, s2)-> s1.length()-s2.length());
String max = optional.get();

3. Streaminterface

O suporte estendido do Java 8 para fluxos é implementado usando a Stream<T>interface, onde Té um parâmetro de tipo que indica o tipo de dados que estão sendo transmitidos no fluxo. Em outras palavras, um fluxo é completamente independente do tipo de dados que ele transmite.

Para obter um objeto de fluxo de uma coleção , basta chamar seu stream()método. O código é mais ou menos assim:

Stream<Type> name = collection.stream();
Obtendo um fluxo de uma coleção

Nesse caso, a coleção será considerada a fonte de dados do fluxo, e o Stream<Type>objeto será uma ferramenta para obter os dados da coleção na forma de um fluxo de dados.

ArrayList<String> list = new ArrayList<String>();
Collections.addAll(list, "Hello", "how's", "life?");
Stream<String> stream = list.stream();

A propósito, você pode obter um fluxo não apenas de coleções, mas também de matrizes . Para fazer isso, você precisa usar o método. Por exemplo:Arrays.stream()

Stream<Type> name = Arrays.stream(array);
Obtendo um stream de um array

Nesse caso, a matriz será considerada a fonte de dados para o fluxo chamado name.

Integer[] array = {1, 2, 3};
Stream<Integer> stream = Arrays.stream(array);

Nenhum dado é movido quando o Stream<Type>objeto é criado. Simplesmente obtemos um objeto de fluxo para começar a construir um pipeline de fluxo.