Algumas das atualizações do Java 8 ao 15
Tags: java
Published: 2021-03-15 10:42:46

E aee jovens, tudo bem com vocês?

Neste artigo eu irei abordar algumas das novidades que foram aparecendo desde a versão 8 do Java até a 15. Não irei adicionar todas as atualizações pois o artigo ficaria muito grande. No me ponto de vista foram as que chamaram mais atenção, mas temos outras novidades bem interessantes também.

Então bora começar.

## Programação Funcional (Java 8)

No Java 8, programação funcional e _lambdas_ foram adicionados como recursos da linguagem. O paradigma principal da [programação funcional](https://www.freecodecamp.org/news/an-introduction-to-the-basic-principles-of-functional-programming-a2c2a15c84/) são valores imutáveis. Os dados passam por um _pipeline_ de etapas de modificação, onde cada etapa pega alguma entrada e mapeia para uma nova saída. A programação funcional pode ser usada com `Streams` e _null-safe_ (`Optional`) em Java, conforme mostrado abaixo ...

## Streams (Java 8)

Para um programa de computador comum, geralmente você precisa trabalhar com uma lista de valores e realizar uma determinada transformação em cada valor. Antes do Java 8, você tinha que usar um loop `for` para essa transformação, mas a partir de agora, você pode usar [Streams](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/stream/Stream.html) da seguinte maneira:

```java
Stream.of("Olá", "Ótimo")
    .map(s -> s + " mundo")
    .forEach(System.out::println);
```

> Olá mundo
> Ótimo mundo

A função `map` recebe como entrada um _lambda_, que será aplicado a todos os elementos no fluxo.

_Streams_ podem funcionar em `List`, `Set` e `Map` (via transformação). Graças ao _Streams_, você pode se livrar de praticamente todos os _loops_ em seu código!

## Optional (Java 8)

Outro problema comum em Java eram as exceções de `Null Pointer`. Portanto, Java introduziu [Optional](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Optional.html) - envolve uma referência que pode ou não ser nula. A aplicação de atualizações a este _Optional_ pode ser feita de maneira funcional:

```java
Optional.of(new Random().nextInt(10))
    .filter (i -> i % 2 == 0)
    .map(i -> "O número é par: " + i)
    .ifPresent(System.out::println);
```

> O número é par: 6

No _snippet_ acima criamos um número aleatório, o envolvemos em um objeto `Optional` e, em seguida, se o número for par ele sera impresso na tela.

## JShell (Java 9)

Finalmente temos um [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop) para Java, e seu nome é [JShell](https://docs.oracle.com/en/java/javase/11/jshell/introduction-jshell.html#GUID-630F27C8-1195-4989-9F6B-2C51D46F52C8)! Em poucas palavras JShell permite experimentar trechos de código Java sem escrever e compilar uma classe completa. Em vez disso, você pode executar um comando por vez e ver o resultado imediatamente. Aqui está um exemplo simples:

```bash
$ <JDK>/bin/jshell
jshell> System.out.println("Olá mundo!")
Olá Mundo!
```

Pessoas familiarizadas com linguagens interpretadas como __JavaScript__ ou __Python__ tiveram o prazer de um _REPL_ por muito tempo, mas até agora, esse recurso estava faltando em Java. __JShell__ permite definir variáveis, mas também entidades mais complexas, como _métodos_ multilinhas, _classes_ e executar _loops_. Além disso, o JShell oferece suporte ao preenchimento automático, o que é útil se você não souber os métodos exatos oferecidos por uma determinada classe Java.

## Factory Methods para Collections imutáveis (Java 9)

A inicialização simples de `List` está ausente em Java há muito tempo, mas esses tempos acabaram agora. Anteriormente, você tinha que fazer algo assim:

```bash
jshell> List<Integer> lista = Arrays.asList(1, 2, 3, 4)
lista ==> [1, 2, 3, 4]
```

Isso agora é simplificado da seguinte forma:

```bash
jshell> List<Integer> list = List.of(1, 2, 3, 4)
b ==> [1, 2, 3, 4]
```

Este método `of(...)` existe para [List](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/List.html), [Set](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Set.html) e [Map](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Map.html). Todos eles criam um objeto imutável em apenas uma simples linha de código.

## Inferencia de tipo com var (Java 10)

Java 10 introduziu a nova palavra-chave [var](https://developer.oracle.com/java/jdk-10-local-variable-type-inference.html) que permite omitir o tipo de uma variável.

```bash
jshell> var x = new HashSet<String>()
x ==> []

jshell> x.add("maçã")
$ 1 ==> verdadeiro
```

No exemplo acima, o tipo de x pode ser inferido como `HashSet` pelo compilador.

Esse recurso ajuda a reduzir o código e melhorar a legibilidade. No entanto, há algumas limitações para isso, você só pode usar `var` dentro do corpo dos métodos, e o compilador inferirá o tipo em tempo de compilação, então tudo ainda está estaticamente tipado.

## Compilação e execução de um arquivo com um único comando (Java 11)

Anteriormente, quando você escrevia um programa Java simples consistindo em um arquivo, era necessário primeiro compilar o arquivo com _javac_ e depois executá-lo com _java_. No Java 11, você pode executar as duas etapas com um comando:

Main.java:
```java
public class Main {
  public static void main(String[] args) {
    System.out.println("Olá mundo!");
  }
}
```

```bash
$ java ./Main.java
Olá Mundo!
```

Para programas simples ou experimentos que consistem em apenas uma classe Java, esse recurso para [executar arquivos de código-fonte único](https://www.baeldung.com/java-single-file-source-code) tornará sua vida mais fácil.


## Expressão Switch (Java 12)

Java 12 nos trouxe [expressões Switch](https://docs.oracle.com/en/java/javase/13/language/switch-expressions.html). Aqui está uma demonstração rápida de como a expressão difere da antiga instrução `switch`.

A antiga instrução `switch` define o fluxo do programa:

```bash
jshell> var i = 3
jshell> String s;
jshell> switch (i) {
   ...> case 1: s = "um"; break;
   ...> case 2: s = "dois"; break;
   ...> case 3: s = "três"; break;
   ...> default: s = "número desconhecido";
   ...>}
jshell> s
s ==> "três"
```

Em contraste, a nova expressão switch retorna um valor:

```bash
jshell> var i = 3;
jshell> var x = switch(i) {
   ...> case 1 -> "um";
   ...> case 2 -> "dois";
   ...> case 3 -> "três";
   ...> default -> "número desconhecido";
   ...>};
x ==> "três"
```

Para resumir, a instrução `switch` antiga define o fluxo do programa e a nova expressão switch retiorna um valor.

Observe que esta nova instrução `switch` é uma espécie de função de mapeamento,  há uma entrada (no caso i acima) e há uma saída (aqui x). Isso é um [Pattern Matching](https://en.wikipedia.org/wiki/Pattern_matching) que ajuda a tornar o Java mais compatível com os princípios de programação funcional. Uma instrução [switch semelhante](https://docs.scala-lang.org/tour/pattern-matching.html) está disponível no __Scala__ há algum tempo.

Algumas coisas a serem observadas:

Em vez de pontos duplos, usamos setas ->
Não há necessidade de `break`
O `case default` pode ser omitido quando todos os casos possíveis são considerados
Para habilitar este recurso com Java 12, use __--enable-preview --source 12__

## Strings multi-linhas (Java 13)

Você já teve que definir uma `String` longa de várias linhas como JSON ou XML? Até agora, você provavelmente colocaria tudo em uma linha e usaria caracteres de nova linha "_\n_", mas isso torna a `String` muito mais difícil de ler. Aí vem o Java 13 com [String de várias linhas](https://blogs.oracle.com/javamagazine/text-blocks-come-to-java)!

Por exemplo:

```java
public class Main {
  public static void main (String[] args) {
    var s = """
      {
         "receita": "Vitamina de melancia",
         "duração": "10 minutos",
         "itens": ["melancia", "limão", "salsa"]
      } """;
    System.out.println(s);
  }
}
```

Agora, executamos o método principal por meio de inicialização de arquivo único:

```bash
java --enable-preview --source 13 Main.java

{
    "receita": "Vitamina de melancia",
    "duração": "10 minutos",
    "itens": ["melancia", "limão", "salsa"]
}
```

A `String` resultante se estende por várias linhas, as aspas "" são deixadas intactas e até mesmo as tabulações são preservadas!

## Classes de dados: Record (Java 14)

De todos os novos recursos neste artigo, este é provavelmente o que estou mais animado, finalmente existem classes de dados em Java! Essas classes são declaradas com a palavra-chave [Record](https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/lang/Record.html) e têm `getters` automáticos, um `construtor`, o método `equals()`, etc. Em suma, você pode se livrar de um grande pedaço de código!

```shell
jshell> record Employee(String nome, int idade, String departamento) {}
|  created record Employee

jshell> var x = new Employee("Richard", 25, "DEV");
x ==> Employee[nome=Richard, idade=25, departamento=DEV]

jshell> x.nome()
$2 ==> "Richard"
```

__Scala__ tem um recurso semelhante com [case classes](https://docs.scala-lang.org/tour/case-classes.html) e __Kotlin__ com [data classes](https://kotlinlang.org/docs/data-classes.html). Em Java, muitos desenvolvedores usam o [Lombok](https://projectlombok.org/) até agora, que ofereceu praticamente os recursos que inspiraram `Record` para o Java 14. Mais detalhes podem ser encontrados neste [artigo do Baeldung](https://www.baeldung.com/java-record-keyword).

## instanceof sem Cast (Java 14)

As versões anteriores do Java já continham a palavra-chave `instanceof`:

```java
Object obj = new String("Olá");
if (obj instanceof String) {
  System.out.println("Comprimento da string:" + ((String) obj).length());
}
```

A parte chata: primeiro verificamos se s é do tipo `String` e, em seguida, transformamos novamente ela em `String` para recuperar seu comprimento.

Agora, com o Java 14, o compilador é inteligente o suficiente para [inferir o tipo automaticamente após o instanceof](https://blogs.oracle.com/javamagazine/pattern-matching-for-instanceof-in-java-14):

```java
Object obj = new String("Olá");
if (obj instanceof String myStr) {
  System.out.println("Comprimento da string:" + myStr.length());
}
```


## Sealed classes (Java 15)

Com a [palavra-chave Sealed](https://docs.oracle.com/en/java/javase/15/language/sealed-classes-and-interfaces.html), você pode restringir quais classes podem estender uma determinada classe ou interface. Aqui está um exemplo:

```java
public sealed interface Fruta permits Banana, Pera {
    String getName();
}

public final class Banana implements Fruit {
    public String getName() { return "Banana"; }
}

public final class Pera implements Fruit {
    public String getName() { return "Pera"; }
}
```

Então, como isso nos ajuda? Bem, agora você sabe quantas frutas existem. Este é realmente um passo importante na direção de [pattern matching totalmente suportado](https://blogs.oracle.com/javamagazine/inside-the-language-sealed-types), onde você pode tratar as `classes` como `Enums`. Este recurso `Sealed` combina perfeitamente com a nova expressão de `Switch` explicada anteriormente.

E com isso finalizamos aqui as atualizações que achei legal levantar, quaisquer dúvidas ou sugestões para que eu melhore nas postagens deixe um comentário.

E aí tem alguma outra atualização que acha legal adicionar?

## Referencias

[Baeldung](https://www.baeldung.com/)
[Documentação Java](https://docs.oracle.com/en/java/javase/11/docs/api/index.html)
[Philipp Gysel](https://dev.to/pmgysel)
[Free Code Camp](https://www.freecodecamp.org/news/an-introduction-to-the-basic-principles-of-functional-programming-a2c2a15c84/)