Java12 min de leitura

Java Moderno: Elevando o Nível com Imutabilidade, Switch Expressions e Pattern Matching

Java Moderno: Elevando o Nível com Imutabilidade, Switch Expressions e Pattern Matching


Introdução

Este artigo fecha um ciclo. Ao longo da série Objects · Immutability · Switch · Pattern Matching, cada tema preparou o terreno para o próximo: começamos nos contratos fundamentais de Object, passamos pela imutabilidade, modernizamos o switch e mergulhamos no Pattern Matching até chegar no ponto onde tudo converge, a exaustividade verificada pelo compilador.

Aqui você encontra as peças conectadas. E o fio condutor é um só: erros que antes explodiam em produção agora nem compilam.

Records, Record Patterns e Sealed/Hidden Classes aparecem de passagem neste artigo porque cada um merece (e vai ganhar) um aprofundamento próprio em artigos dedicados.


1. Objects: os Contratos que Sustentam Tudo

O pitfall clássico: == vs equals()

Todo desenvolvedor Java já caiu ou vai cair nessa armadilha. O operador == compara referências (os dois lados apontam para o mesmo objeto?), enquanto equals() compara conteúdo. O perigo é que às vezes == parece funcionar:

&lt;span class=<span class="hljs-string">&quot;hljs-type&quot;</span>&gt;Integer&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-variable&quot;</span>&gt;a&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">127</span>&lt;/span&gt;;
&lt;span class=<span class="hljs-string">&quot;hljs-type&quot;</span>&gt;Integer&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-variable&quot;</span>&gt;b&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">127</span>&lt;/span&gt;;
System.out.println(a == b); &lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// true (cache de Integer entre -128 e 127)&lt;/span&gt;</span>

&lt;span class=<span class="hljs-string">&quot;hljs-type&quot;</span>&gt;Integer&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-variable&quot;</span>&gt;c&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">128</span>&lt;/span&gt;;
&lt;span class=<span class="hljs-string">&quot;hljs-type&quot;</span>&gt;Integer&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-variable&quot;</span>&gt;d&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">128</span>&lt;/span&gt;;
System.out.println(c == d); &lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// false (fora do cache, objetos distintos)&lt;/span&gt;</span>

&lt;span class=<span class="hljs-string">&quot;hljs-type&quot;</span>&gt;String&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-variable&quot;</span>&gt;x&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;java&amp;quot;&lt;/span&gt;;
&lt;span class=<span class="hljs-string">&quot;hljs-type&quot;</span>&gt;String&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-variable&quot;</span>&gt;y&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;java&amp;quot;&lt;/span&gt;;
System.out.println(x == y); &lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// true (String Constant Pool)&lt;/span&gt;</span>

&lt;span class=<span class="hljs-string">&quot;hljs-type&quot;</span>&gt;String&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-variable&quot;</span>&gt;z&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">new</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-title class_&quot;</span>&gt;String&lt;/span&gt;(&lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;java&amp;quot;&lt;/span&gt;);
System.out.println(x == z); &lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// false (instância fora do pool)&lt;/span&gt;</span>

O código que "funciona" com == graças ao cache de Integer ou ao String Constant Pool é uma bomba-relógio: basta o valor sair da faixa do cache ou a String vir de fora do pool (input do usuário, banco, API) para o comportamento mudar. A regra é simples: para conteúdo, sempre equals().

O novo equals() com instanceof e Flow Scoping

E por falar em equals(), o Pattern Matching aposentou o ritual clássico de implementá-lo. Compare:

&lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// ✗ O ritual antigo&lt;/span&gt;</span>
&lt;span class=<span class="hljs-string">&quot;hljs-meta&quot;</span>&gt;<span class="hljs-meta">@Override</span>&lt;/span&gt;
&lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">public</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-type&quot;</span>&gt;<span class="hljs-type">boolean</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-title function_&quot;</span>&gt;equals&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-params&quot;</span>&gt;(Object obj)&lt;/span&gt; {
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">if</span>&lt;/span&gt; (&lt;span class=<span class="hljs-string">&quot;hljs-built_in&quot;</span>&gt;<span class="hljs-built_in">this</span>&lt;/span&gt; == obj) &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">return</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-literal&quot;</span>&gt;<span class="hljs-literal">true</span>&lt;/span&gt;;
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">if</span>&lt;/span&gt; (obj == &lt;span class=<span class="hljs-string">&quot;hljs-literal&quot;</span>&gt;<span class="hljs-literal">null</span>&lt;/span&gt; || getClass() != obj.getClass()) &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">return</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-literal&quot;</span>&gt;<span class="hljs-literal">false</span>&lt;/span&gt;;
    &lt;span class=<span class="hljs-string">&quot;hljs-type&quot;</span>&gt;Produto&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-variable&quot;</span>&gt;other&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; (Produto) obj;
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">return</span>&lt;/span&gt; nome.equals(other.nome) &amp;amp;&amp;amp; preco.equals(other.preco);
}

&lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// ✓ Com Type Pattern (JDK 16+)&lt;/span&gt;</span>
&lt;span class=<span class="hljs-string">&quot;hljs-meta&quot;</span>&gt;<span class="hljs-meta">@Override</span>&lt;/span&gt;
&lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">public</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-type&quot;</span>&gt;<span class="hljs-type">boolean</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-title function_&quot;</span>&gt;equals&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-params&quot;</span>&gt;(Object obj)&lt;/span&gt; {
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">return</span>&lt;/span&gt; obj &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">instanceof</span>&lt;/span&gt; Produto p
        &amp;amp;&amp;amp; nome.equals(p.nome)
        &amp;amp;&amp;amp; preco.equals(p.preco);
}

Três detalhes fazem a versão nova brilhar:

  • Null-safety de graça: null instanceof Produto retorna false, então a checagem explícita de nulo desaparece
  • Sem cast manual: a binding variable p já nasce com o tipo certo
  • Flow scoping: p só existe onde o compilador prova que o teste passou. No && seguinte, o acesso a p.nome é garantidamente seguro

O flow scoping é mais esperto do que parece. Ele funciona inclusive em negações:

&lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">public</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">void</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-title function_&quot;</span>&gt;processar&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-params&quot;</span>&gt;(Object obj)&lt;/span&gt; {
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">if</span>&lt;/span&gt; (!(obj &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">instanceof</span>&lt;/span&gt; String s)) {
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">return</span>&lt;/span&gt;; &lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// se não é String, sai&lt;/span&gt;</span>
    }
    &lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// s disponível aqui: o compilador sabe que só se chega&lt;/span&gt;</span>
    &lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// neste ponto se o instanceof foi verdadeiro&lt;/span&gt;</span>
    System.out.println(s.toUpperCase());
}

Receiver Parameter: o this explícito que quase ninguém conhece

Fechando o bloco de Objects, um recurso obscuro que rendeu boas discussões na série: desde o Java 8, métodos de instância podem declarar o próprio this como primeiro parâmetro, o chamado receiver parameter:

&lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">public</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;class&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-title class_&quot;</span>&gt;Conta&lt;/span&gt; {
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">public</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">void</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-title function_&quot;</span>&gt;depositar&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-params&quot;</span>&gt;(Conta &lt;span class=<span class="hljs-string">&quot;hljs-built_in&quot;</span>&gt;<span class="hljs-built_in">this</span>&lt;/span&gt;, BigDecimal valor)&lt;/span&gt; {
        &lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// comportamento idêntico ao método sem o receiver parameter&lt;/span&gt;</span>
    }
}

Ele não muda a semântica nem a assinatura em bytecode. Existe por um único motivo: permitir anotações de tipo sobre o this, algo útil para frameworks de análise estática como o Checker Framework:

&lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">public</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">void</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-title function_&quot;</span>&gt;encerrar&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-params&quot;</span>&gt;(&lt;span class=<span class="hljs-string">&quot;hljs-meta&quot;</span>&gt;<span class="hljs-meta">@Locked</span>&lt;/span&gt; Conta &lt;span class=<span class="hljs-string">&quot;hljs-built_in&quot;</span>&gt;<span class="hljs-built_in">this</span>&lt;/span&gt;)&lt;/span&gt; { ... }

É o tipo de detalhe que separa quem decora sintaxe de quem entende o design da linguagem.


2. Immutability: Estado que Não Trai

A série dedicou um artigo inteiro ao custo cognitivo da imutabilidade de Strings: verbos como replace() e toUpperCase() soam como mutação, mas são fábricas de novos objetos. O resultado precisa ser capturado, senão é descartado em silêncio.

A lição se generaliza: imutabilidade elimina categorias inteiras de bug, principalmente em concorrência. E o Java moderno vem empurrando o ecossistema nessa direção em detalhes que passam despercebidos.

Antes e depois: Collectors.toList() vs toList()

Um exemplo perfeito está na Stream API. Compare:

&lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// ✗ Antes (e ainda muito comum em code review)&lt;/span&gt;</span>
List&amp;lt;String&amp;gt; nomes = clientes.stream()
        .map(Cliente::nome)
        .collect(Collectors.toList());

&lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// ✓ Depois (JDK 16+)&lt;/span&gt;</span>
List&amp;lt;String&amp;gt; nomes = clientes.stream()
        .map(Cliente::nome)
        .toList();

A diferença vai além da estética:

Collectors.toList() Stream.toList()
Desde JDK 8 JDK 16
Mutabilidade Sem garantia (hoje retorna ArrayList mutável) Lista não modificável
Contrato Implementação pode mudar Imutabilidade documentada
Verbosidade Import + chamada longa Direto na stream

O detalhe importante: Stream.toList() retorna uma lista não modificável. Qualquer add() ou remove() posterior lança UnsupportedOperationException. Isso é imutabilidade como default, o mesmo princípio das Strings aplicado às coleções que sua pipeline produz. Se o código adiante realmente precisa mutar a lista, a intenção fica explícita: new ArrayList<>(stream.toList()).


3. Switch Expressions: De Instrução a Expressão

O switch clássico era uma instrução de controle de fluxo com uma pegadinha embutida: o fall-through silencioso quando alguém esquece o break. O JEP 361 (JDK 14) transformou o switch em uma expressão que retorna valores.

Arrow syntax: o fim do fall-through acidental

&lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// ✗ Antes: break esquecido = bug silencioso&lt;/span&gt;</span>
String turno;
&lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">switch</span>&lt;/span&gt; (hora) {
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">6</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">7</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">8</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">9</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">10</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">11</span>&lt;/span&gt;:
        turno = &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;manhã&amp;quot;&lt;/span&gt;;
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">break</span>&lt;/span&gt;; &lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// esqueceu? cai no próximo case&lt;/span&gt;</span>
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">12</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">13</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">14</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">15</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">16</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">17</span>&lt;/span&gt;:
        turno = &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;tarde&amp;quot;&lt;/span&gt;;
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">break</span>&lt;/span&gt;;
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">default</span>&lt;/span&gt;:
        turno = &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;noite&amp;quot;&lt;/span&gt;;
}

&lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// ✓ Depois: cada braço é isolado por design&lt;/span&gt;</span>
&lt;span class=<span class="hljs-string">&quot;hljs-type&quot;</span>&gt;String&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-variable&quot;</span>&gt;turno&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">switch</span>&lt;/span&gt; (hora) {
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">6</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">7</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">8</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">9</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">10</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">11</span>&lt;/span&gt;     -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;manhã&amp;quot;&lt;/span&gt;;
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">12</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">13</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">14</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">15</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">16</span>&lt;/span&gt;, &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">17</span>&lt;/span&gt; -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;tarde&amp;quot;&lt;/span&gt;;
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">default</span>&lt;/span&gt;                     -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;noite&amp;quot;&lt;/span&gt;;
};

yield substitui o break

Quando um braço precisa de um bloco com lógica, quem devolve o valor é o yield. E aqui vale desfazer uma confusão comum: yield não é um break renomeado. O break interrompia fluxo; o yield produz o valor da expressão:

&lt;span class=<span class="hljs-string">&quot;hljs-type&quot;</span>&gt;<span class="hljs-type">int</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-variable&quot;</span>&gt;taxa&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">switch</span>&lt;/span&gt; (categoria) {
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; PREMIUM -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">0</span>&lt;/span&gt;;
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; PADRAO  -&amp;gt; {
        &lt;span class=<span class="hljs-string">&quot;hljs-type&quot;</span>&gt;<span class="hljs-type">int</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-variable&quot;</span>&gt;base&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; calcularBase();
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">yield</span>&lt;/span&gt; base + ajusteSazonal(); &lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// yield entrega o valor do braço&lt;/span&gt;</span>
    }
};

A distinção semântica importa: numa switch expression não existe "sair sem valor". Todo caminho precisa produzir um resultado, e o compilador cobra isso. É o primeiro sinal da exaustividade que vamos ver adiante.

case null: o fim do boilerplate if (obj == null)

Por décadas, todo switch sobre referência exigia uma guarda externa, porque seletor nulo lança NullPointerException:

&lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// ✗ O ritual de sempre&lt;/span&gt;</span>
&lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">if</span>&lt;/span&gt; (obj == &lt;span class=<span class="hljs-string">&quot;hljs-literal&quot;</span>&gt;<span class="hljs-literal">null</span>&lt;/span&gt;) {
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">return</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;sem valor&amp;quot;&lt;/span&gt;;
}
&lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">return</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">switch</span>&lt;/span&gt; (obj) { ... };

Com o Pattern Matching para switch (preview no JDK 17 via JEP 406, finalizado no JDK 21 via JEP 441), o nulo virou um rótulo de primeira classe:

&lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// ✓ Nulo tratado dentro do próprio switch&lt;/span&gt;</span>
&lt;span class=<span class="hljs-string">&quot;hljs-type&quot;</span>&gt;String&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-variable&quot;</span>&gt;resultado&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">switch</span>&lt;/span&gt; (obj) {
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-literal&quot;</span>&gt;<span class="hljs-literal">null</span>&lt;/span&gt;     -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;sem valor&amp;quot;&lt;/span&gt;;
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; String s -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;texto: &amp;quot;&lt;/span&gt; + s;
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">default</span>&lt;/span&gt;       -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;outro tipo&amp;quot;&lt;/span&gt;;
};

Sem o case null declarado, o comportamento tradicional se mantém: NullPointerException. Ou seja, código legado não muda de comportamento, mas código novo pode finalmente tratar o nulo onde ele pertence.


4. Desvendando o Pattern Matching

Pattern Matching é o mecanismo que testa a forma de um valor e, no mesmo movimento, extrai dados para binding variables. A pergunta deixa de ser "que tipo é isso?" seguida de um cast, e vira uma única operação declarativa.

O switch aceita qualquer objeto

Até o JDK 20, o seletor do switch era restrito a inteiros, enums, Strings e pouco mais. Com o JEP 441, qualquer referência serve como seletor, e os rótulos aceitam padrões de tipo:

String &lt;span class=<span class="hljs-string">&quot;hljs-title function_&quot;</span>&gt;descreve&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-params&quot;</span>&gt;(Object obj)&lt;/span&gt; {
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">return</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">switch</span>&lt;/span&gt; (obj) {
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; Integer i -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;inteiro de valor &amp;quot;&lt;/span&gt; + i;
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; String s  -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;texto de tamanho &amp;quot;&lt;/span&gt; + s.length();
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-literal&quot;</span>&gt;<span class="hljs-literal">null</span>&lt;/span&gt;      -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;nulo&amp;quot;&lt;/span&gt;;
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">default</span>&lt;/span&gt;        -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;tipo não mapeado&amp;quot;&lt;/span&gt;;
    };
}

O que era uma escada de if-else com instanceof e cast vira uma estrutura onde cada forma possível do dado tem seu lugar declarado.

Guarded Patterns: refinando com when

O padrão de tipo responde "é uma String?". O guarded pattern acrescenta "e ela me interessa?". A cláusula when (JDK 21) anexa uma condição booleana ao padrão, com a binding variable disponível dentro da guarda:

String &lt;span class=<span class="hljs-string">&quot;hljs-title function_&quot;</span>&gt;classifica&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-params&quot;</span>&gt;(Object obj)&lt;/span&gt; {
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">return</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">switch</span>&lt;/span&gt; (obj) {
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; String s &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">when</span>&lt;/span&gt; s.isBlank()     -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;texto em branco&amp;quot;&lt;/span&gt;;
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; String s &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">when</span>&lt;/span&gt; s.length() &amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">10</span>&lt;/span&gt; -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;texto longo&amp;quot;&lt;/span&gt;;
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; String s                      -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;texto curto&amp;quot;&lt;/span&gt;;
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; Integer i &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">when</span>&lt;/span&gt; i &amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">0</span>&lt;/span&gt;          -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;positivo&amp;quot;&lt;/span&gt;;
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; Integer i                     -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;zero ou negativo&amp;quot;&lt;/span&gt;;
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">default</span>&lt;/span&gt;                            -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;outro&amp;quot;&lt;/span&gt;;
    };
}

Dominância: a ordem importa

E o exemplo acima só compila porque respeita a regra de dominância: o primeiro match vence, então padrões mais específicos precisam vir antes dos mais genéricos. Inverta a ordem e o compilador recusa o código:

&lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// ✗ Não compila: String s domina o padrão guardado abaixo&lt;/span&gt;</span>
&lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">switch</span>&lt;/span&gt; (obj) {
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; String s                  -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;qualquer texto&amp;quot;&lt;/span&gt;;
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; String s &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">when</span>&lt;/span&gt; s.isBlank() -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;em branco&amp;quot;&lt;/span&gt;; &lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// inalcançável&lt;/span&gt;</span>
}

A regra prática: constantes e padrões guardados primeiro, padrões de tipo puros depois, o caso mais genérico por último. Diferente do fall-through de antigamente, que falhava em silêncio, aqui a ordem errada é erro de compilação. O compilador de novo agindo antes do bug.

Generics no Pattern Matching: o fantasma do Type Erasure

A série já tinha visitado o Type Erasure, e ele reaparece aqui impondo um limite: não dá para testar o parâmetro genérico em runtime, porque ele não existe mais lá.

&lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// ✗ Não compila: erasure impede verificar o &amp;lt;String&amp;gt; em runtime&lt;/span&gt;</span>
&lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">if</span>&lt;/span&gt; (obj &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">instanceof</span>&lt;/span&gt; List&amp;lt;String&amp;gt; ls) { ... }

&lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// ✓ Compila: o wildcard não promete nada sobre o elemento&lt;/span&gt;</span>
&lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">if</span>&lt;/span&gt; (obj &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">instanceof</span>&lt;/span&gt; List&amp;lt;?&amp;gt; l) { ... }

Dentro de contextos genéricos, porém, o Pattern Matching colabora com a inferência. Um método genérico pode fazer dispatch por tipo mantendo a assinatura flexível:

&lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">static</span>&lt;/span&gt; &amp;lt;T&amp;gt; String &lt;span class=<span class="hljs-string">&quot;hljs-title function_&quot;</span>&gt;processa&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-params&quot;</span>&gt;(T valor)&lt;/span&gt; {
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">return</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">switch</span>&lt;/span&gt; (valor) {
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; Integer i    -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;número: &amp;quot;&lt;/span&gt; + i;
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; String s     -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;texto: &amp;quot;&lt;/span&gt; + s;
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; List&amp;lt;?&amp;gt; l    -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;lista com &amp;quot;&lt;/span&gt; + l.size() + &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot; elementos&amp;quot;&lt;/span&gt;;
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-literal&quot;</span>&gt;<span class="hljs-literal">null</span>&lt;/span&gt;         -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;nulo&amp;quot;&lt;/span&gt;;
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">default</span>&lt;/span&gt;           -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;tipo genérico não mapeado&amp;quot;&lt;/span&gt;;
    };
}

A regra mental: o Pattern Matching enxerga o que sobrevive ao erasure. Tipos brutos e wildcards sim, parâmetros genéricos concretos não.

Pattern Matching + Predicate: composição funcional

Uma combinação elegante que rendeu um post próprio na série: usar Type Patterns dentro de Predicate para construir filtros type-safe e componíveis:

Predicate&amp;lt;Object&amp;gt; textoRelevante =
        obj -&amp;gt; obj &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">instanceof</span>&lt;/span&gt; String s &amp;amp;&amp;amp; !s.isBlank() &amp;amp;&amp;amp; s.length() &amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">3</span>&lt;/span&gt;;

Predicate&amp;lt;Object&amp;gt; numeroPositivo =
        obj -&amp;gt; obj &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">instanceof</span>&lt;/span&gt; Integer i &amp;amp;&amp;amp; i &amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">0</span>&lt;/span&gt;;

List&amp;lt;Object&amp;gt; relevantes = entradas.stream()
        .filter(textoRelevante.or(numeroPositivo))
        .toList();

O binding funciona normalmente dentro da lambda, e a composição via and(), or() e negate() continua disponível. É Pattern Matching saindo do if e do switch para viver no mundo funcional, com o toList() imutável da seção 2 fechando a pipeline.


5. Exaustividade: Onde Tudo se Encaixa

Todo switch com padrões precisa ser exaustivo: cobrir todos os valores possíveis de entrada. Esqueceu um caso? O código nem compila.

Existem três formas de garantir a completude. O default clássico funciona, mas esconde o problema debaixo do tapete. Um padrão total como case Object o é um pega-tudo mais elegante, mas continua pega-tudo. E a terceira forma é onde o compilador mostra seu poder: quando a hierarquia é selada, tratar cada subtipo permitido prova que a lógica está completa, sem default nenhum:

&lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">sealed</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;interface&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-title class_&quot;</span>&gt;Player&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">permits</span>&lt;/span&gt; Tennis, Football { }

String &lt;span class=<span class="hljs-string">&quot;hljs-title function_&quot;</span>&gt;descreve&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-params&quot;</span>&gt;(Player player)&lt;/span&gt; {
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">return</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">switch</span>&lt;/span&gt; (player) {
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; Tennis t   -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;joga tênis&amp;quot;&lt;/span&gt;;
        &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">case</span>&lt;/span&gt; Football f -&amp;gt; &lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;joga futebol&amp;quot;&lt;/span&gt;;
    }; &lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// exaustivo, sem default&lt;/span&gt;</span>
}

A recompensa vem na manutenção: se daqui a seis meses alguém adicionar Golf à hierarquia, todo switch sobre Player quebra na compilação apontando exatamente onde o novo caso não foi tratado. Com um default genérico, o Golf passaria batido e viraria bug silencioso em produção.

error: the switch expression does not cover all possible input values

💡 No IntelliJ, um Alt+Enter no switch quebrado oferece "Add remaining branches" e gera o case que falta. Refatorar hierarquia selada vira uma sequência de Alt+Enter pelo projeto.

Sealed Classes, Hidden Classes, Records e Record Patterns merecem mais do que um parágrafo, e por isso ganham artigos dedicados na sequência. Por ora, fica o essencial: a exaustividade é o ponto onde a série inteira converge. O equals() limpo do bloco de Objects, o estado imutável do bloco de Immutability, o switch que produz valores e o Pattern Matching que decompõe tipos, tudo culmina no compilador provando que nenhum cenário ficou de fora.


Conclusão

O Java moderno foca em expressividade e segurança em tempo de compilação, e a série mostrou esse movimento em camadas. Nos fundamentos, equals() com Type Pattern e flow scoping aposentou o ritual de null-check e cast, enquanto o pitfall de == vs equals() segue lembrando que referência não é conteúdo. Na imutabilidade, das Strings ao toList() da Stream API, o default da linguagem caminha para estado que não trai. No switch, a arrow syntax e o yield acabaram com o fall-through, e o case null enterrou o boilerplate de guarda. E no Pattern Matching, guarded patterns, dominância, generics e Predicates formam um vocabulário novo para expressar lógica de negócio, coroado pela exaustividade.

O padrão comum a tudo: muito mais intenção, e uma classe inteira de bugs eliminada antes do primeiro deploy.

Isso fecha a série Objects · Immutability · Switch · Pattern Matching. E abre a próxima: Records, Record Patterns e Sealed/Hidden Classes em profundidade. O Java continua evoluindo, e quem domina esse alicerce escreve código limpo, seguro e profissional.


Este artigo fecha a série Objects · Immutability · Switch · Pattern Matching do getcaramelo.dev.

Tem dúvidas ou quer aprofundar algum ponto? Me encontra no LinkedIn ou no GitHub.

Publicado por: Guilherme Gomes - 23/07/2026 10:30

Gostou do conteúdo? Me segue no @getcaramelo.dev

← Voltar aos Artigos