Java 15 se tornou GA!

gsuaki

Gabriel Suaki

Posted on September 16, 2020

Java 15 se tornou GA!

Cover image - Java 15

15/09/20 - Java 15 se torna GA.

Além das preview features, conhecidas das versões anteriores (Text Blocks, Pattern Matching para instanceof e Records), foi introduzido no Java 15 os conceitos de sealed classes e Local interfaces & enums.

1. Getting Started

Para começar vamos instalar a JDK 15. Eu utilizo o sdkman para gerenciar todas as JDKs do meu workspace, mas se achar mais conveniente baixar direto da OpenJDK, este é o link.

Utilizando o sdkman, primeiro listamos as JDKs disponíveis:

sdk list java
Enter fullscreen mode Exit fullscreen mode

sdk list java result

Em seguida, basta executar o comando com o identifier encontrado:

sdk install java 15.ea.36-open
Enter fullscreen mode Exit fullscreen mode

Pronto! A JDK está instalada e pronta para ser usada. Agora basta configurar o IntelliJ:

IntelliJ config

2. Sealed Classes & Interfaces (Preview)

Essa funcionalidade permite assumir o controle da hierarquia de classes e interfaces através da keyword sealed e permits. Vamos ao exemplo:

sealed public interface Shape permits Circle, Square, Rectangle, Diamond { }
Enter fullscreen mode Exit fullscreen mode

No código acima, definimos a interface Shape e marcamos como sealed. Classes sealed demandam a declaração da keyword permits para dizer ao compilador quais as classes ou interfaces poderão estender ou implementar Shape. Existe uma exceção a regra somente para casos onde as implementações estão no mesmo arquivo que a sealed class. Quando isto ocorre, podemos omitir a keyword permits da declaração da sealed class.

Classes cuja classe pai é sealed demandam declarar sua abrangência de hierarquia entre: final, non-sealed ou sealed.

Normalmente, as classes filhas serão final para evitar que possam ser herdadas. Ao tentar herdar, observará um erro de compilação.

final class Square implements Shape { }

class TimesSquare extends Square { } // Compilador fica chateado, pois Square é final.
Enter fullscreen mode Exit fullscreen mode

Também é possível substituir o uso da final class por record, pois são classes imutáveis e, por baixo dos panos, o compilador gera um final class.

record Square() implements Shape { }
Enter fullscreen mode Exit fullscreen mode

As estruturas filhas declaradas como non-sealed expandem a hierarquia, possibilitando-as de serem estendidas ou implementadas por qualquer outra classe ou interface.

non-sealed interface Diamond extends Shape { }

non-sealed class Circle implements Shape { }

class BigCircle extends Circle { }

interface ColoredDiamond extends Diamond {}
Enter fullscreen mode Exit fullscreen mode

Deve-se atentar que non-sealed classes podem quebrar a coesão de seus modelos, pois voltam a expor tudo o que tentou proteger com sealed.

Por último, e não menos importante, podemos ter sealed classes implementando sealed classes. Dessa forma, não deixamos tão aberto quanto as non-sealed e nem tão engessado quanto as final, continuamos restringindo a herança ou composição.

sealed class Rectangle implements Shape permits CustomRectangle { }

final class CustomRectangle extends Rectangle { }
Enter fullscreen mode Exit fullscreen mode

2.1. Type-Test Pattern matching

Com o controle de hierarquia que as sealed classes proveem ao compilador, seria possível fazer type-test-pattern igual ao que vemos no when do Kotlin.

public Double getAreaOfShape(final Shape shape) {
    return switch (shape) {
        case Circle c -> circleArea(c);
        case Diamond d -> diamondArea(d);
        case Rectangle r -> rectangleArea(r);
        case Square s -> squareArea(s);
    };
}
Enter fullscreen mode Exit fullscreen mode

Atualmente esse código acima não compila, pois type-test-pattern no switch-case não é uma funcionalidade ainda, é apenas uma possibilidade. A razão disso, é por conta do precedente que foi adicionado ao Java 14: Pattern Matching for instanceof.

Indo mais além, pelo fato do compilador saber exatamente quem implementa determinada sealed class, seria possível omitir o default no switch-case, pois todas as possibilidades foram declaradas.

3. Local interfaces e enums

Com a segunda preview de Records (JEP 384) assomado no Java 15, a possibilidade de criar estruturas no escopo local de um método virou realidade. Além das records, agora podemos declarar classes, enums e interfaces dentro do escopo de métodos também!

  public List<TV> getTop5ExpensiveTV(final List<TV> tvs) {

    enum Price {EXPENSIVE, CHEAP}

    record TVPrice(TV tv, Double amount) { 

      Price price() {
        return amount > 4_000 ? Price.EXPENSIVE : Price.CHEAP;
      }
    }

    return tvs.stream()
        .map(tv -> new TVPrice(tv, calculateAmount(tv)))
        .sorted(Comparator.comparing(TVPrice::price))
        .map(TVPrice::tv)
        .limit(5)
        .collect(Collectors.toList());
  }
Enter fullscreen mode Exit fullscreen mode

O exemplo acima mostra uma forma de usufruir de estruturas locais. Foi utilizado local record e enum para fazer um filtro em uma lista.

5. What’s next?

Existem outras features de melhoria de performance e reimplementações de estruturas, não hesite em ler o release notes completo.

4. Referências

💖 💪 🙅 🚩
gsuaki
Gabriel Suaki

Posted on September 16, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Java 15 se tornou GA!
java Java 15 se tornou GA!

September 16, 2020