<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[9mac.dev blog]]></title><description><![CDATA[9mac.dev blog]]></description><link>https://blog.9mac.dev</link><generator>RSS for Node</generator><lastBuildDate>Fri, 10 Apr 2026 04:55:48 GMT</lastBuildDate><atom:link href="https://blog.9mac.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Java 17 Features Every Senior Developer Should Know - Part 6: Syntax Cheat Sheet & Reference Guide]]></title><description><![CDATA[Complete Reference for Desktop | Print-friendly version for desk reference
This is Part 6 (final) of "Java 17 Features Every Senior Developer Should Know" - a comprehensive reference covering syntax, usage patterns, feature interactions, and migratio...]]></description><link>https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-6-syntax-cheat-sheet-and-reference-guide</link><guid isPermaLink="true">https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-6-syntax-cheat-sheet-and-reference-guide</guid><category><![CDATA[Java]]></category><category><![CDATA[java 17]]></category><dc:creator><![CDATA[Dawid Świst]]></dc:creator><pubDate>Wed, 19 Nov 2025 20:41:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763584813740/d1371748-d504-4edc-a038-4b10b5049ee5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Complete Reference for Desktop</strong> | Print-friendly version for desk reference</p>
<p>This is Part 6 (final) of "Java 17 Features Every Senior Developer Should Know" - a comprehensive reference covering syntax, usage patterns, feature interactions, and migration strategies for all 6 features covered in Parts 1-5. Perfect for printing and keeping at your desk as you work with modern Java code.</p>
<h2 id="heading-how-to-use-this-series">How to Use This Series</h2>
<p><strong>First time learning Java 17 features?</strong></p>
<ul>
<li><p>Read <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-1-introduction-and-var-keyword">Part 1: var Keyword</a> → <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-2-records">Part 2: Records</a> → <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-3-sealed-classes">Part 3: Sealed Classes</a> → <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-4-pattern-matching-and-switch-expressions">Part 4: Pattern Matching</a> → <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-5-text-blocks">Part 5: Text Blocks</a></p>
</li>
<li><p>Each part includes practical examples and best practices</p>
</li>
<li><p>Estimated reading time: 1-2 hours total</p>
</li>
</ul>
<p><strong>Need a quick reference?</strong></p>
<ul>
<li><p>Use the Quick Reference Cards below (this page)</p>
</li>
<li><p>Each section provides syntax, examples, and use cases at a glance</p>
</li>
<li><p>Links to full parts for detailed explanations</p>
</li>
</ul>
<p><strong>Looking for specific feature?</strong></p>
<ul>
<li><p>Use the Table of Contents below to jump to any feature</p>
</li>
<li><p>Each Quick Reference Card links back to its full part</p>
</li>
</ul>
<p><strong>Want to understand feature interactions?</strong></p>
<ul>
<li>See full articles for detailed explanations and real-world patterns</li>
</ul>
<p><strong>Planning a migration?</strong></p>
<ul>
<li>See full articles for phased migration strategy and compatibility notes</li>
</ul>
<hr />
<h2 id="heading-series-overview-amp-timeline">Series Overview &amp; Timeline</h2>
<p>This series covers <strong>6 major Java features spanning Java 10-17</strong>, representing over 20 years of language evolution:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Part</td><td>Feature</td><td>Release</td><td>JEP</td></tr>
</thead>
<tbody>
<tr>
<td>1</td><td><strong>var</strong> - Local variable type inference</td><td>Java 10</td><td>286</td></tr>
<tr>
<td>2</td><td><strong>Records</strong> - Immutable data carriers</td><td>Java 16</td><td>395</td></tr>
<tr>
<td>3</td><td><strong>Sealed Classes</strong> - Controlled inheritance</td><td>Java 17</td><td>409</td></tr>
<tr>
<td>4</td><td><strong>Pattern Matching</strong> - Type check + cast</td><td>Java 16</td><td>394</td></tr>
<tr>
<td>5</td><td><strong>Switch Expressions</strong> - Returns values</td><td>Java 14</td><td>361</td></tr>
<tr>
<td>6</td><td><strong>Text Blocks</strong> - Multi-line strings</td><td>Java 15</td><td>378</td></tr>
</tbody>
</table>
</div><p><strong>Why These Features Matter:</strong></p>
<ul>
<li><p><strong>Part 1-2</strong>: Reduce boilerplate (less code to write)</p>
</li>
<li><p><strong>Part 3-4</strong>: Increase safety (compiler catches more errors)</p>
</li>
<li><p><strong>Part 5-6</strong>: Improve readability (code matches intent)</p>
</li>
</ul>
<hr />
<h2 id="heading-feature-comparison-matrix">Feature Comparison Matrix</h2>
<h3 id="heading-decision-at-a-glance">Decision At-a-Glance</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Challenge</td><td>Solution</td><td>When</td><td>Avoid</td></tr>
</thead>
<tbody>
<tr>
<td>Long generic types</td><td><code>var</code></td><td>Type obvious from right side</td><td>Type unclear from context</td></tr>
<tr>
<td>Immutable data classes</td><td><code>Records</code></td><td>DTOs, value objects</td><td>Mutable data, need inheritance</td></tr>
<tr>
<td>Controlled inheritance</td><td><code>Sealed classes</code></td><td>Fixed type hierarchies</td><td>Open-for-extension APIs</td></tr>
<tr>
<td>Type checking + casting</td><td><code>Pattern matching</code></td><td>Checking types with immediate use</td><td>Polymorphism is better choice</td></tr>
<tr>
<td>Returning from switch</td><td><code>Switch expressions</code></td><td>Enum mapping, value computation</td><td>Simple if-else, need fall-through</td></tr>
<tr>
<td>Multi-line strings</td><td><code>Text blocks</code></td><td>JSON, SQL, HTML, XML</td><td>Single line strings</td></tr>
</tbody>
</table>
</div><h3 id="heading-feature-dependencies">Feature Dependencies</h3>
<pre><code class="lang-plaintext">var (Java 10)
    ↓ (enables clean declaration of)
Records (Java 16)
    ↓ (work well with)
Sealed Classes (Java 17)
    ↓ (enables exhaustive checking with)
Pattern Matching (Java 16)
    ↓ (used in)
Switch Expressions (Java 14)
    ↓ (format data with)
Text Blocks (Java 15)
</code></pre>
<hr />
<h2 id="heading-quick-reference-cards">Quick Reference Cards</h2>
<h3 id="heading-var-keyword-java-10">var Keyword (Java 10)</h3>
<p><strong>JEP 286</strong> | Local variable type inference</p>
<p>📖 <strong>See also</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-1-introduction-and-var-keyword">Part 1: Introduction &amp; var Keyword</a> - Full feature deep dive with examples and pitfalls</p>
<p><strong>Syntax</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">var</span> name = expression;
</code></pre>
<p><strong>✅ Use When</strong></p>
<ul>
<li><p>Type is obvious from right side: <code>var user = new User()</code></p>
</li>
<li><p>Long generic types: <code>var map = new HashMap&lt;String, List&lt;Employee&gt;&gt;()</code></p>
</li>
<li><p>Stream chains: <code>var filtered = list.stream().filter(...)</code></p>
</li>
<li><p>For-loops: <code>for (var item : items)</code></p>
</li>
</ul>
<p><strong>❌ Don't Use When</strong></p>
<ul>
<li><p>Type isn't clear: <code>var data = fetchData()</code> (what type?)</p>
</li>
<li><p>Need supertype: <code>List&lt;String&gt; list = new ArrayList&lt;&gt;()</code> (flexibility)</p>
</li>
<li><p>Fields, parameters, return types (not allowed)</p>
</li>
<li><p>Lambda/method reference alone: <code>var c = String::length</code> (error)</p>
</li>
</ul>
<p><strong>Examples</strong></p>
<pre><code class="lang-java"><span class="hljs-comment">// ✅ Good</span>
<span class="hljs-keyword">var</span> message = <span class="hljs-string">"Hello"</span>;
<span class="hljs-keyword">var</span> count = <span class="hljs-number">42</span>;
<span class="hljs-keyword">var</span> users = List.of(<span class="hljs-string">"Alice"</span>, <span class="hljs-string">"Bob"</span>);
<span class="hljs-keyword">var</span> map = <span class="hljs-keyword">new</span> HashMap&lt;String, Integer&gt;();

<span class="hljs-comment">// ❌ Bad</span>
<span class="hljs-keyword">var</span> x = getData();           <span class="hljs-comment">// unclear type</span>
<span class="hljs-keyword">var</span> y = <span class="hljs-keyword">null</span>;                <span class="hljs-comment">// error: cannot infer</span>
<span class="hljs-keyword">var</span> list = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;(); <span class="hljs-comment">// error: need type args</span>
</code></pre>
<p><strong>Quick Rules</strong></p>
<ul>
<li><p>Compile-time type inference (NOT dynamic typing)</p>
</li>
<li><p>Local variables only</p>
</li>
<li><p>Must have initializer</p>
</li>
<li><p>Type never changes</p>
</li>
</ul>
<hr />
<h2 id="heading-records-java-16">Records (Java 16)</h2>
<p><strong>JEP 395</strong> | Immutable data carriers</p>
<p>📖 <strong>See also</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-2-records">Part 2: Records</a> - Full feature deep dive with examples and best practices</p>
<p><strong>Syntax</strong></p>
<pre><code class="lang-java"><span class="hljs-function">record <span class="hljs-title">Name</span><span class="hljs-params">(Type1 field1, Type2 field2)</span> </span>{
    <span class="hljs-comment">// optional: custom methods, validation</span>
}
</code></pre>
<p><strong>✅ Use When</strong></p>
<ul>
<li><p>Data Transfer Objects (DTOs)</p>
</li>
<li><p>Value objects (Point, Money, Range)</p>
</li>
<li><p>Configuration objects</p>
</li>
<li><p>API request/response models</p>
</li>
<li><p>Return values from methods (multiple values)</p>
</li>
</ul>
<p><strong>❌ Don't Use When</strong></p>
<ul>
<li><p>Need mutability</p>
</li>
<li><p>Need inheritance (records are final)</p>
</li>
<li><p>Want to hide internal structure</p>
</li>
<li><p>Need JavaBeans getters (use <code>field()</code> not <code>getField()</code>)</p>
</li>
<li><p>Complex lazy initialization</p>
</li>
</ul>
<p><strong>Examples</strong></p>
<pre><code class="lang-java"><span class="hljs-comment">// Basic record</span>
<span class="hljs-function">record <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{}

<span class="hljs-comment">// With validation (compact constructor)</span>
<span class="hljs-function">record <span class="hljs-title">Range</span><span class="hljs-params">(<span class="hljs-keyword">int</span> start, <span class="hljs-keyword">int</span> end)</span> </span>{
    <span class="hljs-keyword">public</span> Range {
        <span class="hljs-keyword">if</span> (start &gt; end) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException();
    }
}

<span class="hljs-comment">// With custom methods</span>
<span class="hljs-function">record <span class="hljs-title">Person</span><span class="hljs-params">(String name, <span class="hljs-keyword">int</span> age)</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isAdult</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> age &gt;= <span class="hljs-number">18</span>; }
    <span class="hljs-function"><span class="hljs-keyword">public</span> Person <span class="hljs-title">withAge</span><span class="hljs-params">(<span class="hljs-keyword">int</span> newAge)</span> </span>{ <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Person(name, newAge); }
}

<span class="hljs-comment">// Generic record</span>
record Pair&lt;T, U&gt;(T first, U second) {}

<span class="hljs-comment">// Implementing interface</span>
<span class="hljs-function">record <span class="hljs-title">Circle</span><span class="hljs-params">(<span class="hljs-keyword">double</span> radius)</span> implements Shape </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> <span class="hljs-title">area</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> Math.PI * radius * radius; }
}
</code></pre>
<p><strong>What You Get Automatically</strong></p>
<ul>
<li><p>Constructor: <code>public Point(int x, int y)</code></p>
</li>
<li><p>Accessors: <code>p.x()</code>, <code>p.y()</code> (NOT getX(), getY())</p>
</li>
<li><p><code>equals()</code>, <code>hashCode()</code>, <code>toString()</code></p>
</li>
<li><p>Fields are <code>private final</code></p>
</li>
<li><p>Class is <code>final</code></p>
</li>
</ul>
<p><strong>Quick Rules</strong></p>
<ul>
<li><p>Immutable by default</p>
</li>
<li><p>All fields final</p>
</li>
<li><p>Cannot extend other classes</p>
</li>
<li><p>Can implement interfaces</p>
</li>
<li><p>Defensive copying needed for mutable components</p>
</li>
</ul>
<hr />
<h2 id="heading-sealed-classes-java-17">Sealed Classes (Java 17)</h2>
<p><strong>JEP 409</strong> | Controlled inheritance</p>
<p>📖 <strong>See also</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-3-sealed-classes">Part 3: Sealed Classes</a> - Design patterns and hierarchy control explained</p>
<p><strong>Syntax</strong></p>
<pre><code class="lang-java">sealed <span class="hljs-class"><span class="hljs-keyword">interface</span>/<span class="hljs-title">class</span> <span class="hljs-title">Name</span> <span class="hljs-title">permits</span> <span class="hljs-title">SubType1</span>, <span class="hljs-title">SubType2</span> </span>{
}

<span class="hljs-comment">// Subtypes must be: final, sealed, or non-sealed</span>
</code></pre>
<p><strong>✅ Use When</strong></p>
<ul>
<li><p>Closed type hierarchies (known subtypes only)</p>
</li>
<li><p>Domain modeling with fixed alternatives</p>
</li>
<li><p>Exhaustive pattern matching</p>
</li>
<li><p>API design where you control all implementations</p>
</li>
</ul>
<p><strong>❌ Don't Use When</strong></p>
<ul>
<li><p>Open for extension (third-party implementations)</p>
</li>
<li><p>Plugin architectures</p>
</li>
<li><p>Need unlimited subclassing</p>
</li>
</ul>
<p><strong>Examples</strong></p>
<pre><code class="lang-java"><span class="hljs-comment">// Sealed interface with records</span>
sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Shape</span> <span class="hljs-title">permits</span> <span class="hljs-title">Circle</span>, <span class="hljs-title">Rectangle</span>, <span class="hljs-title">Triangle</span> </span>{}

<span class="hljs-function">record <span class="hljs-title">Circle</span><span class="hljs-params">(<span class="hljs-keyword">double</span> radius)</span> implements Shape </span>{}
<span class="hljs-function">record <span class="hljs-title">Rectangle</span><span class="hljs-params">(<span class="hljs-keyword">double</span> width, <span class="hljs-keyword">double</span> height)</span> implements Shape </span>{}
<span class="hljs-function">record <span class="hljs-title">Triangle</span><span class="hljs-params">(<span class="hljs-keyword">double</span> base, <span class="hljs-keyword">double</span> height)</span> implements Shape </span>{}

<span class="hljs-comment">// Sealed class hierarchy</span>
sealed <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Result</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-title">permits</span> <span class="hljs-title">Success</span>, <span class="hljs-title">Failure</span> </span>{}

<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Success</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-keyword">extends</span> <span class="hljs-title">Result</span>&lt;<span class="hljs-title">T</span>&gt; </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> T value;
    <span class="hljs-comment">// ...</span>
}

<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Failure</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-keyword">extends</span> <span class="hljs-title">Result</span>&lt;<span class="hljs-title">T</span>&gt; </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String error;
    <span class="hljs-comment">// ...</span>
}

<span class="hljs-comment">// With non-sealed for extension point</span>
sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Payment</span> <span class="hljs-title">permits</span> <span class="hljs-title">CreditCard</span>, <span class="hljs-title">DebitCard</span>, <span class="hljs-title">ExtensiblePayment</span> </span>{}
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CreditCard</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Payment</span> </span>{}
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DebitCard</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Payment</span> </span>{}
non-sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">ExtensiblePayment</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Payment</span> </span>{} <span class="hljs-comment">// Open for extension</span>
</code></pre>
<p><strong>Permitted Subtypes Must</strong></p>
<ul>
<li><p>Be in same module (or same package if unnamed module)</p>
</li>
<li><p>Directly extend/implement the sealed type</p>
</li>
<li><p>Be declared as: <code>final</code>, <code>sealed</code>, or <code>non-sealed</code></p>
</li>
</ul>
<p><strong>Quick Rules</strong></p>
<ul>
<li><p>Compiler knows all subtypes</p>
</li>
<li><p>Enables exhaustive pattern matching</p>
</li>
<li><p>Subtypes must be declared in permits clause</p>
</li>
<li><p>Better than marker interfaces for closed sets</p>
</li>
</ul>
<hr />
<h2 id="heading-pattern-matching-java-16">Pattern Matching (Java 16)</h2>
<p><strong>JEP 394</strong> | Pattern matching for instanceof</p>
<p>📖 <strong>See also</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-4-pattern-matching-and-switch-expressions">Part 4: Pattern Matching &amp; Switch Expressions</a> - Type patterns and exhaustiveness checking</p>
<p><strong>Syntax</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> Type variableName) {
    <span class="hljs-comment">// use variableName here</span>
}
</code></pre>
<p><strong>✅ Use When</strong></p>
<ul>
<li><p>Type checking with immediate use</p>
</li>
<li><p>Implementing <code>equals()</code> methods</p>
</li>
<li><p>Polymorphic dispatch</p>
</li>
<li><p>Guard conditions: <code>obj instanceof String s &amp;&amp; s.length() &gt; 5</code></p>
</li>
</ul>
<p><strong>❌ Don't Use When</strong></p>
<ul>
<li><p>Polymorphism would be better (add methods to types)</p>
</li>
<li><p>Type checking indicates design smell</p>
</li>
<li><p>Performance is absolutely critical</p>
</li>
</ul>
<p><strong>Examples</strong></p>
<pre><code class="lang-java"><span class="hljs-comment">// Basic pattern matching</span>
<span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> String s) {
    System.out.println(s.toUpperCase());
}

<span class="hljs-comment">// With guards</span>
<span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> String s &amp;&amp; s.length() &gt; <span class="hljs-number">0</span>) {
    <span class="hljs-keyword">return</span> s.toUpperCase();
}

<span class="hljs-comment">// Simplified equals()</span>
<span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">equals</span><span class="hljs-params">(Object obj)</span> </span>{
    <span class="hljs-keyword">return</span> obj <span class="hljs-keyword">instanceof</span> Point p &amp;&amp; <span class="hljs-keyword">this</span>.x == p.x &amp;&amp; <span class="hljs-keyword">this</span>.y == p.y;
}

<span class="hljs-comment">// Type hierarchy</span>
<span class="hljs-keyword">if</span> (shape <span class="hljs-keyword">instanceof</span> Circle c) {
    <span class="hljs-keyword">return</span> Math.PI * c.radius() * c.radius();
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (shape <span class="hljs-keyword">instanceof</span> Rectangle r) {
    <span class="hljs-keyword">return</span> r.width() * r.height();
}
</code></pre>
<p><strong>Scope Rules</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> String s &amp;&amp; s.length() &gt; <span class="hljs-number">5</span>) {
    <span class="hljs-comment">// 's' in scope here</span>
}

<span class="hljs-keyword">if</span> (!(obj <span class="hljs-keyword">instanceof</span> String s)) {
    <span class="hljs-comment">// 's' NOT in scope</span>
} <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">// 's' IS in scope here</span>
}
</code></pre>
<p><strong>Quick Rules</strong></p>
<ul>
<li><p>Pattern variable scoped by flow analysis</p>
</li>
<li><p>instanceof returns false for null (safe)</p>
</li>
<li><p>No manual cast needed</p>
</li>
<li><p>Combines type check + cast + assignment</p>
</li>
</ul>
<hr />
<h2 id="heading-switch-expressions-java-14">Switch Expressions (Java 14)</h2>
<p><strong>JEP 361</strong> | Switch as expression with arrow syntax</p>
<p>📖 <strong>See also</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-4-pattern-matching-and-switch-expressions">Part 4: Pattern Matching &amp; Switch Expressions</a> - Clean conditional logic with yield</p>
<p><strong>Syntax</strong></p>
<pre><code class="lang-java"><span class="hljs-comment">// Arrow syntax (no fall-through)</span>
<span class="hljs-keyword">var</span> result = <span class="hljs-keyword">switch</span> (value) {
    <span class="hljs-keyword">case</span> LABEL1 -&gt; expression1;
    <span class="hljs-keyword">case</span> LABEL2, LABEL3 -&gt; expression2;
    <span class="hljs-keyword">default</span> -&gt; expressionN;
};

<span class="hljs-comment">// Multi-statement with yield</span>
<span class="hljs-keyword">var</span> result = <span class="hljs-keyword">switch</span> (value) {
    <span class="hljs-keyword">case</span> LABEL1 -&gt; {
        <span class="hljs-comment">// statements</span>
        yield value1;
    }
};
</code></pre>
<p><strong>✅ Use When</strong></p>
<ul>
<li><p>Mapping enum values</p>
</li>
<li><p>Returning values from branches</p>
</li>
<li><p>Exhaustive enum matching (no default needed)</p>
</li>
<li><p>Multi-way conditional logic</p>
</li>
</ul>
<p><strong>❌ Don't Use When</strong></p>
<ul>
<li><p>Simple if-else would suffice</p>
</li>
<li><p>Need fall-through (use colon syntax)</p>
</li>
<li><p>Cases are complex and unrelated</p>
</li>
</ul>
<p><strong>Examples</strong></p>
<pre><code class="lang-java"><span class="hljs-comment">// Basic switch expression</span>
String dayType = <span class="hljs-keyword">switch</span> (day) {
    <span class="hljs-keyword">case</span> MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -&gt; <span class="hljs-string">"Weekday"</span>;
    <span class="hljs-keyword">case</span> SATURDAY, SUNDAY -&gt; <span class="hljs-string">"Weekend"</span>;
};

<span class="hljs-comment">// With yield</span>
<span class="hljs-keyword">int</span> result = <span class="hljs-keyword">switch</span> (op) {
    <span class="hljs-keyword">case</span> ADD -&gt; {
        System.out.println(<span class="hljs-string">"Adding"</span>);
        yield a + b;
    }
    <span class="hljs-keyword">case</span> SUBTRACT -&gt; {
        System.out.println(<span class="hljs-string">"Subtracting"</span>);
        yield a - b;
    }
    <span class="hljs-keyword">default</span> -&gt; <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException();
};

<span class="hljs-comment">// Exhaustive enum (no default needed)</span>
<span class="hljs-keyword">int</span> days = <span class="hljs-keyword">switch</span> (season) {
    <span class="hljs-keyword">case</span> SPRING, SUMMER, FALL, WINTER -&gt; <span class="hljs-number">3</span>;
};
</code></pre>
<p><strong>Quick Rules</strong></p>
<ul>
<li><p>Arrow <code>-&gt;</code> prevents fall-through</p>
</li>
<li><p>Must be exhaustive (all cases or default)</p>
</li>
<li><p>Use <code>yield</code> for multi-statement branches</p>
</li>
<li><p>No <code>break</code> needed with arrows</p>
</li>
<li><p>Can mix with pattern matching (future Java)</p>
</li>
</ul>
<hr />
<h2 id="heading-text-blocks-java-15">Text Blocks (Java 15)</h2>
<p><strong>JEP 378</strong> | Multi-line string literals</p>
<p>📖 <strong>See also</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-5-text-blocks">Part 5: Text Blocks</a> - Multi-line strings with security considerations</p>
<p><strong>Syntax</strong></p>
<pre><code class="lang-java">String text = <span class="hljs-string">""</span><span class="hljs-string">"
    content here
    more content
    "</span><span class="hljs-string">""</span>;
</code></pre>
<p><strong>✅ Use When</strong></p>
<ul>
<li><p>Multi-line strings (JSON, SQL, HTML, XML)</p>
</li>
<li><p>Embedded code snippets</p>
</li>
<li><p>Long error messages</p>
</li>
<li><p>Test fixtures</p>
</li>
</ul>
<p><strong>❌ Don't Use When</strong></p>
<ul>
<li><p>Single-line strings</p>
</li>
<li><p>Dynamically built strings</p>
</li>
<li><p>Need platform-specific line endings</p>
</li>
</ul>
<p><strong>Examples</strong></p>
<pre><code class="lang-java"><span class="hljs-comment">// JSON</span>
String json = <span class="hljs-string">""</span><span class="hljs-string">"
    {
      "</span>name<span class="hljs-string">": "</span>Alice<span class="hljs-string">",
      "</span>age<span class="hljs-string">": 30,
      "</span>email<span class="hljs-string">": "</span>alice<span class="hljs-meta">@example</span>.com<span class="hljs-string">"
    }"</span><span class="hljs-string">""</span>;

<span class="hljs-comment">// SQL</span>
String sql = <span class="hljs-string">""</span><span class="hljs-string">"
    SELECT u.name, u.email, o.total
    FROM users u
    JOIN orders o ON u.id = o.user_id
    WHERE o.status = 'COMPLETED'
    ORDER BY o.created_at DESC"</span><span class="hljs-string">""</span>;

<span class="hljs-comment">// With formatted()</span>
String json = <span class="hljs-string">""</span><span class="hljs-string">"
    {
      "</span>name<span class="hljs-string">": "</span>%s<span class="hljs-string">",
      "</span>age<span class="hljs-string">": %d
    }"</span><span class="hljs-string">""</span>.formatted(name, age);

<span class="hljs-comment">// Indentation control</span>
String text = <span class="hljs-string">""</span><span class="hljs-string">"
    Line 1
        Indented line 2
    Line 3
    "</span><span class="hljs-string">""</span>;  <span class="hljs-comment">// Closing delimiter determines indent stripping</span>

<span class="hljs-comment">// Line continuation</span>
String <span class="hljs-keyword">long</span> = <span class="hljs-string">""</span><span class="hljs-string">"
    This is a long line that \
    continues on the next line \
    in source code.
    "</span><span class="hljs-string">""</span>;

<span class="hljs-comment">// Essential space</span>
String trailing = <span class="hljs-string">""</span><span class="hljs-string">"
    Line with space:\s
    "</span><span class="hljs-string">""</span>;
</code></pre>
<p><strong>Special Escapes</strong></p>
<ul>
<li><p><code>\s</code> - Essential space (survives whitespace stripping)</p>
</li>
<li><p><code>\</code> - Line continuation (no newline inserted)</p>
</li>
<li><p>Standard escapes work: <code>\n</code>, <code>\t</code>, <code>\"</code>, <code>\\</code></p>
</li>
</ul>
<p><strong>Quick Rules</strong></p>
<ul>
<li><p>Opening <code>"""</code> must be followed by newline</p>
</li>
<li><p>Closing <code>"""</code> position determines indent</p>
</li>
<li><p>All line endings normalized to <code>\n</code></p>
</li>
<li><p>Trailing whitespace auto-removed</p>
</li>
<li><p>Common indent automatically stripped</p>
</li>
</ul>
<hr />
<p><strong>End of Series</strong></p>
<p>This concludes "Java 17 Features Every Senior Developer Should Know"</p>
<ul>
<li><p><strong>Part 1</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-1-introduction-and-var-keyword">var Keyword</a></p>
</li>
<li><p><strong>Part 2</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-2-records">Records</a></p>
</li>
<li><p><strong>Part 3</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-3-sealed-classes">Sealed Classes</a></p>
</li>
<li><p><strong>Part 4</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-4-pattern-matching-and-switch-expressions">Pattern Matching &amp; Switch Expressions</a></p>
</li>
<li><p><strong>Part 5</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-5-text-blocks">Text Blocks</a></p>
</li>
<li><p><strong>Part 6</strong>: Syntax Cheat Sheet (this document)</p>
</li>
</ul>
<hr />
<h2 id="heading-series-navigation">Series Navigation</h2>
<ul>
<li><p><strong>←</strong> <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-5-text-blocks"><strong>Part 5: Text Blocks</strong></a> - Multi-line string literals</p>
</li>
<li><p><strong>←</strong> <a target="_blank" href="https://blog.9mac.dev/series/java-and-jvm"><strong>Series Overview</strong></a> - Overview and series guide</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Java 17 Features Every Senior Developer Should Know - Part 5: Text Blocks]]></title><description><![CDATA[Welcome to Part 5 of our comprehensive series on Java 17 features! In previous installments, we explored the var keyword for type inference, Records for eliminating boilerplate, Sealed Classes for controlled hierarchies, and Pattern Matching & Switch...]]></description><link>https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-5-text-blocks</link><guid isPermaLink="true">https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-5-text-blocks</guid><category><![CDATA[Java]]></category><category><![CDATA[java 17]]></category><dc:creator><![CDATA[Dawid Świst]]></dc:creator><pubDate>Wed, 19 Nov 2025 17:48:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763574478099/cbb32029-80c5-4dbb-b474-e38c8fe2f7e1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome to Part 5 of our comprehensive series on Java 17 features! In previous installments, we explored the <a target="_blank" href="part-1-introduction-and-var.md"><code>var</code> keyword</a> for type inference, Records for eliminating boilerplate, Sealed Classes for controlled hierarchies, and Pattern Matching &amp; Switch Expressions for safer conditionals. Today, we're examining <strong>Text Blocks</strong> (JEP 378), which improves readability when embedding multi-line strings like JSON, SQL, HTML, and XML in Java code.</p>
<p>Traditionally, embedding multi-line strings required extensive use of escape sequences and concatenation operators. Each newline needed an explicit <code>\n</code>, quotes required escaping with <code>\"</code>, and each line demanded a <code>+</code> operator. This approach made code difficult to read and maintain, particularly for structured data formats.</p>
<p>Text blocks provide a cleaner solution. You can directly embed multi-line strings with proper formatting preserved. The compiler automatically handles line termination normalization, indentation stripping, and whitespace management. When combined with <code>formatted()</code>, text blocks offer a practical lightweight alternative for generating emails, invoices, configurations, and other templated content.</p>
<hr />
<h2 id="heading-the-problem-multi-line-strings-before-java-15">The Problem: Multi-line Strings Before Java 15</h2>
<p>Before Java 15, creating multi-line string literals required verbose and error-prone approaches. Consider these common scenarios:</p>
<p><strong>JSON embedded in tests:</strong></p>
<pre><code class="lang-java">String json = <span class="hljs-string">"{\n"</span> +
              <span class="hljs-string">"  \"name\": \"Alice\",\n"</span> +
              <span class="hljs-string">"  \"age\": 30,\n"</span> +
              <span class="hljs-string">"  \"email\": \"alice@example.com\"\n"</span> +
              <span class="hljs-string">"}"</span>;
</code></pre>
<p><strong>SQL queries as strings:</strong></p>
<pre><code class="lang-java">String query = <span class="hljs-string">"SELECT u.name, u.email, o.total\n"</span> +
               <span class="hljs-string">"FROM users u\n"</span> +
               <span class="hljs-string">"JOIN orders o ON u.id = o.user_id\n"</span> +
               <span class="hljs-string">"WHERE o.status = 'COMPLETED'\n"</span> +
               <span class="hljs-string">"ORDER BY o.created_at DESC"</span>;
</code></pre>
<p><strong>HTML templates in code:</strong></p>
<pre><code class="lang-java">String html = <span class="hljs-string">"&lt;html&gt;\n"</span> +
              <span class="hljs-string">"  &lt;head&gt;\n"</span> +
              <span class="hljs-string">"    &lt;title&gt;Welcome&lt;/title&gt;\n"</span> +
              <span class="hljs-string">"  &lt;/head&gt;\n"</span> +
              <span class="hljs-string">"  &lt;body&gt;\n"</span> +
              <span class="hljs-string">"    &lt;h1&gt;Hello, World!&lt;/h1&gt;\n"</span> +
              <span class="hljs-string">"  &lt;/body&gt;\n"</span> +
              <span class="hljs-string">"&lt;/html&gt;"</span>;
</code></pre>
<p><strong>Common issues with this approach:</strong></p>
<ul>
<li><p>Every line requires a <code>+</code> concatenation operator</p>
</li>
<li><p>Newlines must be explicitly written as <code>\n</code></p>
</li>
<li><p>All quotes require escaping with <code>\"</code></p>
</li>
<li><p>Easy to introduce bugs: missing <code>+</code>, forgotten quotes, misplaced escapes</p>
</li>
<li><p>Source code indentation doesn't match the actual data structure</p>
</li>
<li><p>Copy-pasting content from external tools (Postman, database clients, design tools) requires extensive reformatting</p>
</li>
<li><p>Structure and readability of JSON, SQL, or HTML is obscured by Java syntax noise</p>
</li>
<li><p>Maintenance burden: any update to the content requires modifying multiple lines</p>
</li>
</ul>
<hr />
<h2 id="heading-what-are-text-blocks">What Are Text Blocks?</h2>
<p>A <strong>text block</strong> is a multi-line string literal that avoids most escape sequences. It was introduced in Java 15 (JEP 378) after two preview versions in Java 13 and 14.</p>
<p>Text blocks use <strong>triple-double-quote</strong> delimiters (<code>"""</code>) and automatically:</p>
<ul>
<li><p>Normalize line terminators to <code>\n</code></p>
</li>
<li><p>Strip common leading whitespace (indentation)</p>
</li>
<li><p>Remove incidental trailing whitespace</p>
</li>
<li><p>Preserve essential formatting</p>
</li>
</ul>
<h3 id="heading-before-and-after">Before and After</h3>
<pre><code class="lang-java"><span class="hljs-comment">// Before: Traditional string</span>
String json = <span class="hljs-string">"{\n"</span> +
              <span class="hljs-string">"  \"name\": \"Alice\",\n"</span> +
              <span class="hljs-string">"  \"age\": 30\n"</span> +
              <span class="hljs-string">"}"</span>;

<span class="hljs-comment">// After: Text block</span>
String json = <span class="hljs-string">""</span><span class="hljs-string">"
    {
      "</span>name<span class="hljs-string">": "</span>Alice<span class="hljs-string">",
      "</span>age<span class="hljs-string">": 30
    }
    "</span><span class="hljs-string">""</span>;
</code></pre>
<p>The text block version is:</p>
<ul>
<li><p>✅ More readable</p>
</li>
<li><p>✅ No escape sequences needed</p>
</li>
<li><p>✅ Natural indentation</p>
</li>
<li><p>✅ Easier to maintain</p>
</li>
</ul>
<hr />
<h2 id="heading-syntax-and-basic-usage">Syntax and Basic Usage</h2>
<h3 id="heading-delimiter-rules">Delimiter Rules</h3>
<p>Text blocks are delimited by <strong>three double-quote marks</strong> (<code>"""</code>):</p>
<pre><code class="lang-java">String textBlock = <span class="hljs-string">""</span><span class="hljs-string">"
    Line 1
    Line 2
    Line 3
    "</span><span class="hljs-string">""</span>;
</code></pre>
<p><strong>Important rules:</strong></p>
<ol>
<li><p>Opening <code>"""</code> <strong>must</strong> be followed by a line terminator (newline)</p>
</li>
<li><p>Content starts on the line <strong>after</strong> the opening delimiter</p>
</li>
<li><p>Closing <code>"""</code> can be on its own line or after content</p>
</li>
</ol>
<pre><code class="lang-java"><span class="hljs-comment">// ✅ Valid: Opening delimiter on separate line</span>
String valid1 = <span class="hljs-string">""</span><span class="hljs-string">"
    Hello, World!
    "</span><span class="hljs-string">""</span>;

<span class="hljs-comment">// ✅ Valid: Closing delimiter on same line as content</span>
String valid2 = <span class="hljs-string">""</span><span class="hljs-string">"
    Hello, World!"</span><span class="hljs-string">""</span>;

<span class="hljs-comment">// ❌ Invalid: Content on same line as opening delimiter</span>
String invalid = <span class="hljs-string">""</span><span class="hljs-string">"Hello, World!
    "</span><span class="hljs-string">""</span>;
</code></pre>
<h3 id="heading-line-terminator-normalization">Line Terminator Normalization</h3>
<p>All line terminators (Windows <code>\r\n</code>, Unix <code>\n</code>, Mac <code>\r</code>) are normalized to <code>\n</code>:</p>
<pre><code class="lang-java">String block = <span class="hljs-string">""</span><span class="hljs-string">"
    Line 1
    Line 2
    "</span><span class="hljs-string">""</span>;

<span class="hljs-comment">// Equivalent to:</span>
String traditional = <span class="hljs-string">"Line 1\nLine 2\n"</span>;
</code></pre>
<hr />
<h2 id="heading-indentation-rules">Indentation Rules</h2>
<p>Text blocks have sophisticated indentation handling through <strong>automatic indent stripping</strong>. This is the most complex but powerful feature.</p>
<h3 id="heading-rule-1-common-indent-stripping">Rule 1: Common Indent Stripping</h3>
<p>The compiler finds the <strong>minimum indentation</strong> across all non-blank lines and removes it:</p>
<pre><code class="lang-java">String example = <span class="hljs-string">""</span><span class="hljs-string">"
        Hello
        World
        "</span><span class="hljs-string">""</span>;

<span class="hljs-comment">// Actual content: "Hello\nWorld\n"</span>
<span class="hljs-comment">// The 8 leading spaces were stripped because</span>
<span class="hljs-comment">// all lines shared that minimum indentation</span>
</code></pre>
<h3 id="heading-rule-2-closing-delimiter-position-matters">Rule 2: Closing Delimiter Position Matters</h3>
<p>The position of the closing <code>"""</code> determines the indent level:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Closing delimiter aligned left - no stripping</span>
String left = <span class="hljs-string">""</span><span class="hljs-string">"
Hello
World
"</span><span class="hljs-string">""</span>;
<span class="hljs-comment">// Content: "Hello\nWorld\n"</span>

<span class="hljs-comment">// Closing delimiter indented - strips that amount</span>
String indented = <span class="hljs-string">""</span><span class="hljs-string">"
    Hello
    World
    "</span><span class="hljs-string">""</span>;
<span class="hljs-comment">// Content: "Hello\nWorld\n" (4 spaces stripped from each line)</span>
</code></pre>
<h3 id="heading-rule-3-essential-whitespace-with-s">Rule 3: Essential Whitespace with <code>\s</code></h3>
<p>Use <code>\s</code> to preserve spaces that would otherwise be stripped:</p>
<pre><code class="lang-java">String preserved = <span class="hljs-string">""</span><span class="hljs-string">"
    Hello\s\s\sWorld
    "</span><span class="hljs-string">""</span>;
<span class="hljs-comment">// Content: "Hello   World\n" (3 spaces preserved)</span>
</code></pre>
<h3 id="heading-rule-4-trailing-whitespace-removal">Rule 4: Trailing Whitespace Removal</h3>
<p>Spaces at the end of lines are automatically removed (unless escaped with <code>\s</code>):</p>
<pre><code class="lang-java">String trailing = <span class="hljs-string">""</span><span class="hljs-string">"
    Hello
    World
    "</span><span class="hljs-string">""</span>;
<span class="hljs-comment">// Content: "Hello\nWorld\n" (trailing spaces removed)</span>
</code></pre>
<hr />
<h2 id="heading-escape-sequences">Escape Sequences</h2>
<p>Text blocks support standard Java escape sequences plus two new ones.</p>
<h3 id="heading-standard-escapes">Standard Escapes</h3>
<p>All traditional escapes still work:</p>
<pre><code class="lang-java">String escapes = <span class="hljs-string">""</span><span class="hljs-string">"
    Newline: \n
    Tab: \t
    Backslash: \\
    Quote: \"
    "</span><span class="hljs-string">""</span>;
</code></pre>
<h3 id="heading-new-escape-1-essential-space-s">New Escape 1: Essential Space (<code>\s</code>)</h3>
<p>Marks a space that survives trailing whitespace removal:</p>
<pre><code class="lang-java">String essential = <span class="hljs-string">""</span><span class="hljs-string">"
    End with space:\s
    "</span><span class="hljs-string">""</span>;
<span class="hljs-comment">// Content: "End with space: \n" (space preserved)</span>
</code></pre>
<h3 id="heading-new-escape-2-line-continuation">New Escape 2: Line Continuation (<code>\</code>)</h3>
<p>Ends a line without adding <code>\n</code>:</p>
<pre><code class="lang-java">String continued = <span class="hljs-string">""</span><span class="hljs-string">"
    This is a very long sentence that we want to \
    break across multiple lines in source code \
    but appear as one line in the string.
    "</span><span class="hljs-string">""</span>;
<span class="hljs-comment">// Content: "This is a very long sentence that we want to break across multiple lines in source code but appear as one line in the string.\n"</span>
</code></pre>
<h3 id="heading-escaping-triple-quotes">Escaping Triple Quotes</h3>
<p>To include <code>"""</code> in a text block, escape at least one quote:</p>
<pre><code class="lang-java">String quotes = <span class="hljs-string">""</span><span class="hljs-string">"
    He said: \""</span><span class="hljs-string">"Hello!\""</span><span class="hljs-string">"
    "</span><span class="hljs-string">""</span>;
<span class="hljs-comment">// Content: "He said: \"\"\"Hello!\"\"\"\n"</span>
</code></pre>
<hr />
<h2 id="heading-practical-examples">Practical Examples</h2>
<p>Let's explore text blocks through eight comprehensive examples, including critical security considerations for SQL queries.</p>
<h3 id="heading-example-1-basic-text-blocks">Example 1: Basic Text Blocks</h3>
<p>Text blocks eliminate concatenation and escape sequences for multi-line text.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BasicTextBlocksExample</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getMultilineText</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            Line 1
            Line 2
            Line 3
            "</span><span class="hljs-string">""</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getPoem</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            Roses are "</span>red<span class="hljs-string">",
            Violets are "</span>blue<span class="hljs-string">",
            Java 15 is awesome,
            And so are you!"</span><span class="hljs-string">""</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getIndentedBlock</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
                This line has 4 spaces of indent
                    This line has 8 spaces
                Back to 4 spaces
                "</span><span class="hljs-string">""</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        System.out.println(<span class="hljs-string">"=== Multi-line Text ==="</span>);
        System.out.println(getMultilineText());

        System.out.println(<span class="hljs-string">"=== Poem with Quotes ==="</span>);
        System.out.println(getPoem());

        System.out.println(<span class="hljs-string">"=== Indented Block ==="</span>);
        System.out.println(getIndentedBlock());
    }
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">=== Multi-line Text ===
Line 1
Line 2
Line 3

=== Poem with Quotes ===
Roses are "red",
Violets are "blue",
Java 15 is awesome,
And so are you!

=== Indented Block ===
This line has 4 spaces of indent
    This line has 8 spaces
Back to 4 spaces
</code></pre>
<p><strong>Key Insight</strong>: Text blocks eliminate the need for <code>\n</code> and <code>\"</code> escape sequences, making multi-line strings dramatically more readable while producing identical runtime results.</p>
<hr />
<h3 id="heading-example-2-json-with-text-blocks">Example 2: JSON with Text Blocks</h3>
<p>Text blocks are perfect for embedded JSON - no more escape sequence hell.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">JSONTextBlocksExample</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getUserJSON</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            {
              "</span>name<span class="hljs-string">": "</span>Alice Smith<span class="hljs-string">",
              "</span>age<span class="hljs-string">": 30,
              "</span>email<span class="hljs-string">": "</span>alice<span class="hljs-meta">@example</span>.com<span class="hljs-string">"
            }"</span><span class="hljs-string">""</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getUserJSONFormatted</span><span class="hljs-params">(String name, <span class="hljs-keyword">int</span> age, String email)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            {
              "</span>name<span class="hljs-string">": "</span>%s<span class="hljs-string">",
              "</span>age<span class="hljs-string">": %d,
              "</span>email<span class="hljs-string">": "</span>%s<span class="hljs-string">"
            }"</span><span class="hljs-string">""</span>.formatted(name, age, email);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        System.out.println(<span class="hljs-string">"=== User JSON ==="</span>);
        System.out.println(getUserJSON());

        System.out.println(<span class="hljs-string">"\n=== Formatted User JSON ==="</span>);
        System.out.println(getUserJSONFormatted(<span class="hljs-string">"Charlie"</span>, <span class="hljs-number">25</span>, <span class="hljs-string">"charlie@example.com"</span>));
    }
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">=== User JSON ===
{
  "name": "Alice Smith",
  "age": 30,
  "email": "alice@example.com"
}

=== Formatted User JSON ===
{
  "name": "Charlie",
  "age": 25,
  "email": "charlie@example.com"
}
</code></pre>
<p><strong>Key Insight</strong>: Text blocks combined with <code>formatted()</code> provide a clean alternative to JSON libraries for simple cases, making test fixtures and configuration templates dramatically more maintainable.</p>
<hr />
<h3 id="heading-example-3-sql-with-text-blocks">Example 3: SQL with Text Blocks</h3>
<p>Text blocks excel at SQL queries, preserving structure and formatting without concatenation hell.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SQLTextBlocksExample</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getActiveOrdersQuery</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            SELECT u.id, u.name, o.order_id, o.total
            FROM users u
            JOIN orders o ON u.id = o.user_id
            WHERE o.status = 'COMPLETED'
            ORDER BY o.created_at DESC"</span><span class="hljs-string">""</span>;
    }

    <span class="hljs-comment">// EDUCATIONAL EXAMPLE: Shows how formatted() works with SQL</span>
    <span class="hljs-comment">// ⚠️ WARNING: This approach is UNSAFE for production code!</span>
    <span class="hljs-comment">// See "SQL Queries with Text Blocks - Security First" section for secure approach</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getOrdersByStatusQuery</span><span class="hljs-params">(String status, <span class="hljs-keyword">double</span> minAmount)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            SELECT * FROM orders
            WHERE status = '%s'
              AND total &gt; %s
            ORDER BY created_at DESC"</span><span class="hljs-string">""</span>.formatted(status, minAmount);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        System.out.println(<span class="hljs-string">"=== Active Orders ==="</span> );
        System.out.println(getActiveOrdersQuery());

        System.out.println(<span class="hljs-string">"\n=== Orders by Status ==="</span>);
        System.out.println(getOrdersByStatusQuery(<span class="hljs-string">"COMPLETED"</span>, <span class="hljs-number">100.0</span>));
    }
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">=== Active Orders ===
SELECT u.id, u.name, o.order_id, o.total
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.status = 'COMPLETED'
ORDER BY o.created_at DESC

=== Orders by Status ===
SELECT * FROM orders
WHERE status = 'COMPLETED'
  AND total &gt; 100.0
ORDER BY created_at DESC
</code></pre>
<p><strong>Key Insight</strong>: Text blocks make SQL queries readable and maintainable by preserving their natural structure, but beware of SQL injection when using <code>formatted()</code> with user input—always use PreparedStatement for production code.</p>
<hr />
<h3 id="heading-example-4-html-and-svg-with-text-blocks">Example 4: HTML and SVG with Text Blocks</h3>
<p>Text blocks simplify working with HTML and XML-like markup languages by eliminating quote escaping and concatenation noise.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HTMLTextBlocksExample</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getDashboardHTML</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            &lt;html&gt;
              &lt;head&gt;
                &lt;title&gt;User Dashboard&lt;/title&gt;
              &lt;/head&gt;
              &lt;body&gt;
                &lt;h1&gt;Welcome, User!&lt;/h1&gt;
                &lt;p&gt;This is your dashboard.&lt;/p&gt;
              &lt;/body&gt;
            &lt;/html&gt;"</span><span class="hljs-string">""</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getSVGCircle</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            &lt;svg width="</span><span class="hljs-number">100</span><span class="hljs-string">" height="</span><span class="hljs-number">100</span><span class="hljs-string">"&gt;
              &lt;circle cx="</span><span class="hljs-number">50</span><span class="hljs-string">" cy="</span><span class="hljs-number">50</span><span class="hljs-string">" r="</span><span class="hljs-number">40</span><span class="hljs-string">" fill="</span>blue<span class="hljs-string">" /&gt;
              &lt;text x="</span><span class="hljs-number">50</span><span class="hljs-string">" y="</span><span class="hljs-number">55</span><span class="hljs-string">" text-anchor="</span>middle<span class="hljs-string">" fill="</span>white<span class="hljs-string">"&gt;SVG&lt;/text&gt;
            &lt;/svg&gt;"</span><span class="hljs-string">""</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        System.out.println(<span class="hljs-string">"=== Dashboard HTML ==="</span>);
        System.out.println(getDashboardHTML());

        System.out.println(<span class="hljs-string">"\n=== SVG Circle ==="</span>);
        System.out.println(getSVGCircle());
    }
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">=== Dashboard HTML ===
&lt;html&gt;
  &lt;head&gt;
    &lt;title&gt;User Dashboard&lt;/title&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;h1&gt;Welcome, User!&lt;/h1&gt;
    &lt;p&gt;This is your dashboard.&lt;/p&gt;
  &lt;/body&gt;
&lt;/html&gt;

=== SVG Circle ===
&lt;svg width="100" height="100"&gt;
  &lt;circle cx="50" cy="50" r="40" fill="blue" /&gt;
  &lt;text x="50" y="55" text-anchor="middle" fill="white"&gt;SVG&lt;/text&gt;
&lt;/svg&gt;
</code></pre>
<p><strong>Key Insight</strong>: Text blocks eliminate the impedance mismatch between HTML/XML structure in design tools and Java code, making templates maintainable without escaping gymnastics.</p>
<hr />
<h3 id="heading-example-5-indentation-rules-and-edge-cases">Example 5: Indentation Rules and Edge Cases</h3>
<p>Text blocks handle indentation intelligently, stripping common leading whitespace while preserving relative indentation for formatting.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">IndentationExample</span> </span>{

    <span class="hljs-comment">// Common indent stripping - closing delimiter determines indent level</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getStripExample</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            Line 1
            Line 2
            Line 3
            "</span><span class="hljs-string">""</span>;
    }

    <span class="hljs-comment">// Closing delimiter aligned left - minimal stripping</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getLeftAlignedClosing</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
    Indented line 1
    Indented line 2
"</span><span class="hljs-string">""</span>;
    }

    <span class="hljs-comment">// Essential whitespace with \s</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getEssentialSpace</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            Line with trailing space:\s
            Another line
            "</span><span class="hljs-string">""</span>;
    }

    <span class="hljs-comment">// Relative indentation preserved</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getRelativeIndent</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            Level 1
                Level 2
                    Level 3
                Level 2
            Level 1
            "</span><span class="hljs-string">""</span>;
    }

    <span class="hljs-comment">// Empty lines handling</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getEmptyLines</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            First paragraph.

            Second paragraph after blank line.

            Third paragraph.
            "</span><span class="hljs-string">""</span>;
    }

    <span class="hljs-comment">// Line continuation with backslash</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getLineContinuation</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            This is a very long line that we want to \
            split in source code but keep as \
            one line in the string.
            "</span><span class="hljs-string">""</span>;
    }

    <span class="hljs-comment">// Mixed example with all features</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getMixedExample</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            Regular line
                Indented line
            Line with essential space:\s
            Split across \
            multiple source lines
            "</span><span class="hljs-string">""</span>;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        System.out.println(<span class="hljs-string">"=== Strip Example ==="</span>);
        System.out.println(<span class="hljs-string">"["</span> + getStripExample() + <span class="hljs-string">"]"</span>);

        System.out.println(<span class="hljs-string">"\n=== Left Aligned Closing ==="</span>);
        System.out.println(<span class="hljs-string">"["</span> + getLeftAlignedClosing() + <span class="hljs-string">"]"</span>);

        System.out.println(<span class="hljs-string">"\n=== Essential Space ==="</span>);
        System.out.println(<span class="hljs-string">"["</span> + getEssentialSpace() + <span class="hljs-string">"]"</span>);

        System.out.println(<span class="hljs-string">"\n=== Line Continuation ==="</span>);
        System.out.println(<span class="hljs-string">"["</span> + getLineContinuation() + <span class="hljs-string">"]"</span>);

        System.out.println(<span class="hljs-string">"\n=== Relative Indent ==="</span>);
        System.out.println(getRelativeIndent());

        System.out.println(<span class="hljs-string">"\n=== Empty Lines ==="</span>);
        System.out.println(getEmptyLines());

        System.out.println(<span class="hljs-string">"\n=== Mixed Example ==="</span>);
        System.out.println(<span class="hljs-string">"["</span> + getMixedExample() + <span class="hljs-string">"]"</span>);
    }
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">=== Strip Example ===
[Line 1
Line 2
Line 3
]

=== Left Aligned Closing ===
[Indented line 1
Indented line 2
]

=== Essential Space ===
[Line with trailing space:
Another line
]

=== Line Continuation ===
[This is a very long line that we want to split in source code but keep as one line in the string.
]

=== Relative Indent ===
Level 1
    Level 2
        Level 3
    Level 2
Level 1

=== Empty Lines ===
First paragraph.

Second paragraph after blank line.

Third paragraph.

=== Mixed Example ===
[Regular line
    Indented line
Line with essential space:
Split across multiple source lines
]
</code></pre>
<p><strong>Key Insight</strong>: Text block indentation rules are designed to balance source code readability with content integrity—the closing delimiter position determines stripping level, preserving intentional formatting.</p>
<hr />
<h3 id="heading-example-6-escape-sequences-and-special-cases">Example 6: Escape Sequences and Special Cases</h3>
<p>Text blocks support all standard Java escape sequences plus two new ones for advanced use cases: <code>\s</code> for essential spaces and <code>\</code> for line continuation without newlines.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EscapeSequencesExample</span> </span>{

    <span class="hljs-comment">// Standard escapes - all traditional escapes work in text blocks</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getStandardEscapes</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            Newline escape: \\n works here
            Tab escape: \\t indents like this
            Backslash: \\\\ is a single backslash
            Quote: \\"</span> doesn<span class="hljs-string">'t need escaping in text blocks
            """;
    }

    // Essential space (\s) - preserves trailing whitespace
    public static String getEssentialSpace() {
        return """
            Line ending with essential space:\\s
            Line without space
            Another line with space:\\s
            """;
    }

    // Line continuation (\) - joins lines without newline
    public static String getLineContinuation() {
        return """
            This is a long sentence that spans \
            multiple lines in source code but \
            appears as a single line in the string.""";
    }

    // Escaping triple quotes
    public static String getTripleQuoteEscape() {
        return """
            He said: \\"\\"\\"Hello!\\"\\"\\"
            And I replied: \\"\\"\\"Hi there!\\"\\"\\"
            """;
    }

    // Practical: SQL with special characters
    public static String getSQLWithSpecialChars() {
        return """
            SELECT * FROM table
            WHERE name = '</span>John\\<span class="hljs-string">'s Data'</span>
            AND description LIKE <span class="hljs-string">'%\\\\%'</span>
            <span class="hljs-string">""</span><span class="hljs-string">";
    }

    // Unicode and emoji handling - text blocks preserve all Unicode characters
    public static String getUnicodeExample() {
        return "</span><span class="hljs-string">""</span>
            Polish characters: ąćęłńóśźż ĄĆĘŁŃÓŚŹŻ
            Czech characters: čřšžď ČŘŠŽĎ
            Greek letters: α β γ δ ε ζ η θ
            Math symbols: ∑ ∫ √ ∞ ≈ ≠ ≥ ≤
            Currency: € £ ¥ ₿ ₹ ₽
            Emoji: 🚀 ✅ ❌ 🎯 🔥 💡 ⚠️ 🔒
            Arrows: → ← ↑ ↓ ⇒ ⇐ ⇑ ⇓
            <span class="hljs-string">""</span><span class="hljs-string">";
    }

    public static void main(String[] args) {
        System.out.println("</span>=== Standard Escapes ===<span class="hljs-string">");
        System.out.println(getStandardEscapes());

        System.out.println("</span>\n=== Essential Space ===<span class="hljs-string">");
        System.out.println("</span>[<span class="hljs-string">" + getEssentialSpace() + "</span>]<span class="hljs-string">");

        System.out.println("</span>\n=== Line Continuation ===<span class="hljs-string">");
        System.out.println("</span>[<span class="hljs-string">" + getLineContinuation() + "</span>]<span class="hljs-string">");

        System.out.println("</span>\n=== Triple Quote Escape ===<span class="hljs-string">");
        System.out.println(getTripleQuoteEscape());

        System.out.println("</span>\n=== SQL with Special Chars ===<span class="hljs-string">");
        System.out.println(getSQLWithSpecialChars());

        System.out.println("</span>\n=== Unicode and Emoji ===<span class="hljs-string">");
        System.out.println(getUnicodeExample());
    }
}</span>
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">=== Standard Escapes ===
Newline escape: \n works here
Tab escape: \t indents like this
Backslash: \ is a single backslash
Quote: " doesn't need escaping in text blocks

=== Essential Space ===
[Line ending with essential space:
Line without space
Another line with space:
]

=== Line Continuation ===
[This is a long sentence that spans multiple lines in source code but appears as a single line in the string.
]

=== Triple Quote Escape ===
He said: """Hello!"""
And I replied: """Hi there!"""

=== SQL with Special Chars ===
SELECT * FROM table
WHERE name = 'John\'s Data'
AND description LIKE '%\%'

=== Unicode and Emoji ===
Polish characters: ąćęłńóśźż ĄĆĘŁŃÓŚŹŻ
Czech characters: čřšžď ČŘŠŽĎ
Greek letters: α β γ δ ε ζ η θ
Math symbols: ∑ ∫ √ ∞ ≈ ≠ ≥ ≤
Currency: € £ ¥ ₿ ₹ ₽
Emoji: 🚀 ✅ ❌ 🎯 🔥 💡 ⚠️ 🔒
Arrows: → ← ↑ ↓ ⇒ ⇐ ⇑ ⇓
</code></pre>
<p><strong>Key Insight</strong>: Text blocks support both standard escape sequences (for compatibility) and new special escapes (<code>\s</code>, <code>\</code>) that provide fine-grained control over whitespace and line handling—enabling both readability and precision.</p>
<hr />
<h3 id="heading-example-7-safe-templates-emails-invoices-and-configuration">Example 7: Safe Templates - Emails, Invoices, and Configuration</h3>
<p>Text blocks combined with <code>formatted()</code> create a powerful lightweight template system without external libraries—ideal for emails, invoices, configuration files, and API responses. These use cases are safe because they don't involve SQL queries.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SimpleTemplateSystemExample</span> </span>{

    <span class="hljs-comment">// Email template with variable interpolation</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">renderEmailTemplate</span><span class="hljs-params">(String recipientName, String activationUrl, String expirationHours)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            &lt;html&gt;
              &lt;body style="</span>font-family: Arial, sans-serif;<span class="hljs-string">"&gt;
                &lt;h2&gt;Welcome, %s!&lt;/h2&gt;
                &lt;p&gt;Thank you for signing up. Please activate your account within %s hours.&lt;/p&gt;
                &lt;p&gt;&lt;a href="</span>%s<span class="hljs-string">" style="</span>background-color: #<span class="hljs-number">007</span>bff; color: white; padding: <span class="hljs-number">10</span>px <span class="hljs-number">20</span>px; text-decoration: none; border-radius: <span class="hljs-number">5</span>px;<span class="hljs-string">"&gt;Activate Account&lt;/a&gt;&lt;/p&gt;
                &lt;p&gt;If you didn't sign up, please ignore this email.&lt;/p&gt;
                &lt;p&gt;Best regards,&lt;br&gt;The Team&lt;/p&gt;
              &lt;/body&gt;
            &lt;/html&gt;"</span><span class="hljs-string">""</span>.formatted(recipientName, expirationHours, activationUrl);
    }

    <span class="hljs-comment">// Invoice template with multiple variables</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">renderInvoiceTemplate</span><span class="hljs-params">(String invoiceNumber, String customerName, String amount, String dueDate)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            ╔════════════════════════════════════════╗
            ║           INVOICE                      ║
            ╚════════════════════════════════════════╝

            Invoice #: %s
            Customer:  %s

            Amount Due: $%s
            Due Date:   %s

            ────────────────────────────────────────
            Thank you for your business!
            "</span><span class="hljs-string">""</span>.formatted(invoiceNumber, customerName, amount, dueDate);
    }

    <span class="hljs-comment">// API response template</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">renderAPIResponseTemplate</span><span class="hljs-params">(String status, String requestId, String message)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            {
              "</span>status<span class="hljs-string">": "</span>%s<span class="hljs-string">",
              "</span>requestId<span class="hljs-string">": "</span>%s<span class="hljs-string">",
              "</span>message<span class="hljs-string">": "</span>%s<span class="hljs-string">",
              "</span>timestamp<span class="hljs-string">": "</span>%s<span class="hljs-string">"
            }"</span><span class="hljs-string">""</span>.formatted(status, requestId, message, java.time.Instant.now().toString());
    }

    <span class="hljs-comment">// Config file template</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">renderConfigTemplate</span><span class="hljs-params">(String appName, String environment, String port)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            # Configuration for %s
            # Environment: %s

            app.name=%s
            app.environment=%s
            server.port=%s
            logging.level=INFO
            "</span><span class="hljs-string">""</span>.formatted(appName, environment, appName, environment, port);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        System.out.println(<span class="hljs-string">"=== Email Template ==="</span>);
        System.out.println(renderEmailTemplate(<span class="hljs-string">"John Smith"</span>, <span class="hljs-string">"https://app.example.com/activate?token=xyz123"</span>, <span class="hljs-string">"24"</span>));

        System.out.println(<span class="hljs-string">"\n=== Invoice Template ==="</span>);
        System.out.println(renderInvoiceTemplate(<span class="hljs-string">"INV-2024-001"</span>, <span class="hljs-string">"Acme Corp"</span>, <span class="hljs-string">"1,500.00"</span>, <span class="hljs-string">"2024-12-31"</span>));

        System.out.println(<span class="hljs-string">"\n=== API Response Template ==="</span>);
        System.out.println(renderAPIResponseTemplate(<span class="hljs-string">"success"</span>, <span class="hljs-string">"req-12345"</span>, <span class="hljs-string">"User created successfully"</span>));

        System.out.println(<span class="hljs-string">"\n=== Config Template ==="</span>);
        System.out.println(renderConfigTemplate(<span class="hljs-string">"MyApp"</span>, <span class="hljs-string">"production"</span>, <span class="hljs-string">"8080"</span>));
    }
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">=== Email Template ===
&lt;html&gt;
  &lt;body style="font-family: Arial, sans-serif;"&gt;
    &lt;h2&gt;Welcome, John Smith!&lt;/h2&gt;
    &lt;p&gt;Thank you for signing up. Please activate your account within 24 hours.&lt;/p&gt;
    &lt;p&gt;&lt;a href="https://app.example.com/activate?token=xyz123" style="background-color: #007bff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px;"&gt;Activate Account&lt;/a&gt;&lt;/p&gt;
    &lt;p&gt;If you didn't sign up, please ignore this email.&lt;/p&gt;
    &lt;p&gt;Best regards,&lt;br&gt;The Team&lt;/p&gt;
  &lt;/body&gt;
&lt;/html&gt;

=== Invoice Template ===
╔════════════════════════════════════════╗
║           INVOICE                      ║
╚════════════════════════════════════════╝

Invoice #: INV-2024-001
Customer:  Acme Corp

Amount Due: $1,500.00
Due Date:   2024-12-31

────────────────────────────────────────
Thank you for your business!

=== API Response Template ===
{
  "status": "success",
  "requestId": "req-12345",
  "message": "User created successfully",
  "timestamp": "2024-11-17T..."
}

=== Config Template ===
# Configuration for MyApp
# Environment: production

app.name=MyApp
app.environment=production
server.port=8080
logging.level=INFO
</code></pre>
<p><strong>Key Insight</strong>: Text blocks combined with <code>formatted()</code> eliminate the need for template engines in simple scenarios—turning Java into a practical choice for generating emails, configurations, and structured text documents with clean, readable code.</p>
<hr />
<h3 id="heading-example-8-secure-sql-with-preparedstatement">Example 8: Secure SQL with PreparedStatement</h3>
<p>Text blocks work perfectly with PreparedStatement for secure, readable SQL queries. This is the SAFE way to combine text blocks with SQL—using parameterized queries instead of string formatting.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SecureSQLExample</span> </span>{

    <span class="hljs-comment">// Safe SQL: Text block with PreparedStatement placeholders (?)</span>
    <span class="hljs-comment">// Text blocks improve readability while PreparedStatement prevents SQL injection</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getOrdersByStatusQuerySafe</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            SELECT o.order_id, o.total, u.name, u.email
            FROM orders o
            JOIN users u ON o.user_id = u.id
            WHERE o.status = ?
              AND o.total &gt;= ?
            ORDER BY o.created_at DESC
            LIMIT ?"</span><span class="hljs-string">""</span>;
    }

    <span class="hljs-comment">// Complex CTE query demonstrating text block power with security</span>
    <span class="hljs-comment">// Common Table Expressions (CTEs) are highly readable with text blocks</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getTopCustomersQuerySafe</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">""</span><span class="hljs-string">"
            WITH customer_totals AS (
                SELECT u.id, u.name, u.email,
                       SUM(o.total) as total_spent,
                       COUNT(o.order_id) as order_count
                FROM users u
                JOIN orders o ON u.id = o.user_id
                WHERE o.status = ?
                  AND o.created_at &gt;= ?
                GROUP BY u.id, u.name, u.email
            )
            SELECT id, name, email, total_spent, order_count
            FROM customer_totals
            WHERE total_spent &gt;= ?
            ORDER BY total_spent DESC
            LIMIT ?"</span><span class="hljs-string">""</span>;
    }

    <span class="hljs-comment">// Example execution method showing PreparedStatement usage</span>
    <span class="hljs-comment">// In production, this would execute against a real database</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> List&lt;String&gt; <span class="hljs-title">executeOrderQuery</span><span class="hljs-params">(Connection conn, String status, <span class="hljs-keyword">double</span> minAmount, <span class="hljs-keyword">int</span> limit)</span>
            <span class="hljs-keyword">throws</span> SQLException </span>{
        List&lt;String&gt; results = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();
        String query = getOrdersByStatusQuerySafe();

        <span class="hljs-comment">// PreparedStatement automatically escapes parameters - SAFE from SQL injection</span>
        <span class="hljs-keyword">try</span> (PreparedStatement ps = conn.prepareStatement(query)) {
            ps.setString(<span class="hljs-number">1</span>, status);      <span class="hljs-comment">// Parameter 1: status</span>
            ps.setDouble(<span class="hljs-number">2</span>, minAmount);   <span class="hljs-comment">// Parameter 2: minAmount</span>
            ps.setInt(<span class="hljs-number">3</span>, limit);          <span class="hljs-comment">// Parameter 3: limit</span>

            <span class="hljs-keyword">try</span> (ResultSet rs = ps.executeQuery()) {
                <span class="hljs-keyword">while</span> (rs.next()) {
                    results.add(String.format(<span class="hljs-string">"Order %s: $%.2f - %s (%s)"</span>,
                        rs.getString(<span class="hljs-string">"order_id"</span>),
                        rs.getDouble(<span class="hljs-string">"total"</span>),
                        rs.getString(<span class="hljs-string">"name"</span>),
                        rs.getString(<span class="hljs-string">"email"</span>)));
                }
            }
        }
        <span class="hljs-keyword">return</span> results;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        System.out.println(<span class="hljs-string">"=== Safe SQL with PreparedStatement ==="</span>);
        System.out.println(getOrdersByStatusQuerySafe());

        System.out.println(<span class="hljs-string">"\n=== Complex CTE Query ==="</span>);
        System.out.println(getTopCustomersQuerySafe());

        System.out.println(<span class="hljs-string">"\n=== Key Points ==="</span>);
        System.out.println(<span class="hljs-string">"1. Use ? placeholders instead of %s formatting"</span>);
        System.out.println(<span class="hljs-string">"2. PreparedStatement.setString/setDouble/setInt for parameters"</span>);
        System.out.println(<span class="hljs-string">"3. Text blocks still improve readability"</span>);
        System.out.println(<span class="hljs-string">"4. Completely safe from SQL injection attacks"</span>);
    }
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">=== Safe SQL with PreparedStatement ===
SELECT o.order_id, o.total, u.name, u.email
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE o.status = ?
  AND o.total &gt;= ?
ORDER BY o.created_at DESC
LIMIT ?

=== Complex CTE Query ===
WITH customer_totals AS (
    SELECT u.id, u.name, u.email,
           SUM(o.total) as total_spent,
           COUNT(o.order_id) as order_count
    FROM users u
    JOIN orders o ON u.id = o.user_id
    WHERE o.status = ?
      AND o.created_at &gt;= ?
    GROUP BY u.id, u.name, u.email
)
SELECT id, name, email, total_spent, order_count
FROM customer_totals
WHERE total_spent &gt;= ?
ORDER BY total_spent DESC
LIMIT ?

=== Key Points ===
1. Use ? placeholders instead of %s formatting
2. PreparedStatement.setString/setDouble/setInt for parameters
3. Text blocks still improve readability
4. Completely safe from SQL injection attacks
</code></pre>
<p><strong>Key Insight</strong>: Text blocks and PreparedStatement are the perfect combination—text blocks provide readability for complex SQL while PreparedStatement ensures security through parameterized queries.</p>
<hr />
<h2 id="heading-template-systems-with-variable-interpolation">Template Systems with Variable Interpolation</h2>
<p>Text blocks combined with <code>formatted()</code> create lightweight template systems without external dependencies. Example 7 demonstrates practical use cases:</p>
<p><strong>Common Use Cases for</strong> <code>formatted()</code> with Text Blocks:</p>
<ol>
<li><p><strong>Email Templates</strong> - Dynamic email content with user data</p>
</li>
<li><p><strong>Invoice/Receipt Generation</strong> - Formatted business documents</p>
</li>
<li><p><strong>Configuration Files</strong> - Properties and values with variables</p>
</li>
<li><p><strong>API Response Templates</strong> - JSON/XML data structures</p>
</li>
<li><p><strong>Log Messages</strong> - Contextual information with parameters</p>
</li>
</ol>
<p>For security considerations when using <code>formatted()</code> with SQL or HTML, see the <a class="post-section-overview" href="#security-considerations">Security Considerations</a> section in Best Practices.</p>
<hr />
<h2 id="heading-best-practices">Best Practices</h2>
<h3 id="heading-security-considerations">Security Considerations</h3>
<p>Text blocks with <code>formatted()</code> introduce critical security risks when used improperly with SQL queries or HTML content. Understanding these risks is essential for production code.</p>
<h4 id="heading-sql-injection-risks">SQL Injection Risks</h4>
<p><strong>CRITICAL</strong>: Using <code>formatted()</code> with SQL queries creates <strong>SQL injection vulnerabilities</strong>—one of the most dangerous security mistakes in application development.</p>
<h5 id="heading-the-danger">The Danger</h5>
<p>When you embed user input directly into SQL strings using <code>formatted()</code>, attackers can manipulate the query:</p>
<pre><code class="lang-java"><span class="hljs-comment">// ❌ DANGEROUS - DO NOT USE IN PRODUCTION</span>
String status = userInput;  <span class="hljs-comment">// User enters: "COMPLETED' OR '1'='1"</span>
String sql = <span class="hljs-string">""</span><span class="hljs-string">"
    SELECT * FROM orders
    WHERE status = '%s'
    "</span><span class="hljs-string">""</span>.formatted(status);
<span class="hljs-comment">// Resulting SQL: SELECT * FROM orders WHERE status = 'COMPLETED' OR '1'='1'</span>
<span class="hljs-comment">// This returns ALL orders, bypassing the status filter!</span>
</code></pre>
<p>More severe attacks can delete data, access sensitive information, or compromise your entire database:</p>
<pre><code class="lang-java"><span class="hljs-comment">// ❌ If user enters: "'; DROP TABLE orders; --"</span>
String sql = <span class="hljs-string">""</span><span class="hljs-string">"
    SELECT * FROM orders WHERE status = '%s'
    "</span><span class="hljs-string">""</span>.formatted(maliciousInput);
<span class="hljs-comment">// Resulting SQL executes: SELECT * FROM orders WHERE status = ''; DROP TABLE orders; --'</span>
<span class="hljs-comment">// Your entire orders table is DELETED!</span>
</code></pre>
<h5 id="heading-the-safe-sql-pattern">The Safe SQL Pattern</h5>
<p><strong>Always use PreparedStatement</strong> for SQL with dynamic values:</p>
<pre><code class="lang-java"><span class="hljs-comment">// ✅ SAFE - Use PreparedStatement with text blocks</span>
String query = <span class="hljs-string">""</span><span class="hljs-string">"
    SELECT * FROM orders
    WHERE status = ?
      AND total &gt;= ?
    "</span><span class="hljs-string">""</span>;

<span class="hljs-keyword">try</span> (PreparedStatement ps = connection.prepareStatement(query)) {
    ps.setString(<span class="hljs-number">1</span>, userInput);  <span class="hljs-comment">// Automatically escaped - safe!</span>
    ps.setDouble(<span class="hljs-number">2</span>, minAmount);
    <span class="hljs-keyword">try</span> (ResultSet rs = ps.executeQuery()) {
        <span class="hljs-comment">// Process results</span>
    }
}
</code></pre>
<p><strong>Key Points:</strong></p>
<ul>
<li><p>Text blocks improve SQL readability</p>
</li>
<li><p><code>PreparedStatement</code> prevents SQL injection</p>
</li>
<li><p>Use <code>?</code> placeholders, NOT <code>%s</code> formatting</p>
</li>
<li><p>Set parameters via <code>setString()</code>, <code>setInt()</code>, <code>setDouble()</code>, etc.</p>
</li>
</ul>
<p><strong>See Example 8</strong> (SecureSQLExample) for complete, production-ready code demonstrating safe SQL with text blocks and PreparedStatement.</p>
<h5 id="heading-when-text-blocks-are-safe-with-sql">When Text Blocks ARE Safe with SQL</h5>
<p>Text blocks are perfectly safe for SQL when:</p>
<ol>
<li><p><strong>Static queries</strong> (no variables): <code>String query = """SELECT * FROM users""";</code></p>
</li>
<li><p><strong>PreparedStatement placeholders</strong>: <code>String query = """WHERE id = ?""";</code> (parameters set via <code>setString/setInt/etc</code>)</p>
</li>
<li><p><strong>Non-executable purposes</strong>: Logging, documentation, display-only queries</p>
</li>
</ol>
<h5 id="heading-when-text-blocks-are-unsafe-with-sql">When Text Blocks Are UNSAFE with SQL</h5>
<p>Never use <code>formatted()</code> with SQL when:</p>
<ol>
<li><p><strong>Any user input</strong> is involved (form fields, URL parameters, API requests)</p>
</li>
<li><p><strong>External data sources</strong> (file uploads, API responses, message queues)</p>
</li>
<li><p><strong>Any untrusted data</strong> that could be manipulated</p>
</li>
</ol>
<h4 id="heading-htmlxss-injection-risks">HTML/XSS Injection Risks</h4>
<p><strong>WARNING</strong>: Using <code>formatted()</code> with HTML and user input creates <strong>Cross-Site Scripting (XSS) vulnerabilities</strong>.</p>
<h5 id="heading-the-danger-1">The Danger</h5>
<p>When you embed user input directly into HTML using <code>formatted()</code>, attackers can inject malicious scripts:</p>
<pre><code class="lang-java"><span class="hljs-comment">// ❌ DANGEROUS - XSS Vulnerability</span>
String username = userInput;  <span class="hljs-comment">// User enters: "&lt;script&gt;alert('XSS')&lt;/script&gt;"</span>
String html = <span class="hljs-string">""</span><span class="hljs-string">"
    &lt;html&gt;
      &lt;body&gt;
        &lt;h1&gt;Welcome, %s!&lt;/h1&gt;
      &lt;/body&gt;
    &lt;/html&gt;"</span><span class="hljs-string">""</span>.formatted(username);
<span class="hljs-comment">// Resulting HTML: &lt;h1&gt;Welcome, &lt;script&gt;alert('XSS')&lt;/script&gt;!&lt;/h1&gt;</span>
<span class="hljs-comment">// The script executes in the browser, potentially stealing cookies or performing unauthorized actions!</span>
</code></pre>
<h5 id="heading-the-safe-html-pattern">The Safe HTML Pattern</h5>
<p><strong>Always escape HTML entities</strong> when inserting user input into HTML:</p>
<pre><code class="lang-java"><span class="hljs-comment">// ✅ SAFE - Escape HTML entities before formatting</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">escapeHtml</span><span class="hljs-params">(String input)</span> </span>{
    <span class="hljs-keyword">return</span> input
        .replace(<span class="hljs-string">"&amp;"</span>, <span class="hljs-string">"&amp;amp;"</span>)
        .replace(<span class="hljs-string">"&lt;"</span>, <span class="hljs-string">"&amp;lt;"</span>)
        .replace(<span class="hljs-string">"&gt;"</span>, <span class="hljs-string">"&amp;gt;"</span>)
        .replace(<span class="hljs-string">"\""</span>, <span class="hljs-string">"&amp;quot;"</span>)
        .replace(<span class="hljs-string">"'"</span>, <span class="hljs-string">"&amp;#x27;"</span>);
}

String username = escapeHtml(userInput);  <span class="hljs-comment">// Converts &lt;script&gt; to &amp;lt;script&amp;gt;</span>
String html = <span class="hljs-string">""</span><span class="hljs-string">"
    &lt;html&gt;
      &lt;body&gt;
        &lt;h1&gt;Welcome, %s!&lt;/h1&gt;
      &lt;/body&gt;
    &lt;/html&gt;"</span><span class="hljs-string">""</span>.formatted(username);
<span class="hljs-comment">// Resulting HTML: &lt;h1&gt;Welcome, &amp;lt;script&amp;gt;alert('XSS')&amp;lt;/script&amp;gt;!&lt;/h1&gt;</span>
<span class="hljs-comment">// The script is displayed as text, not executed!</span>
</code></pre>
<p><strong>Alternative</strong>: Use dedicated HTML templating libraries (Thymeleaf, Freemarker) that automatically escape variables and provide built-in XSS protection.</p>
<p><strong>See Example 7</strong> (SimpleTemplateSystemExample) for safe template patterns with non-executable content like emails and invoices.</p>
<h4 id="heading-safe-use-cases-for-formatted">Safe Use Cases for formatted()</h4>
<p><code>formatted()</code> with text blocks is <strong>completely safe</strong> for:</p>
<ol>
<li><p><strong>Email Templates</strong> - User input is content, not executable code</p>
</li>
<li><p><strong>Invoice/Receipt Generation</strong> - Display-only data</p>
</li>
<li><p><strong>Configuration Files</strong> - Properties and values, no SQL/HTML</p>
</li>
<li><p><strong>API Response Templates</strong> - JSON/XML data structures (ensure proper JSON escaping if needed)</p>
</li>
<li><p><strong>Log Messages</strong> - Contextual information for debugging</p>
</li>
</ol>
<h4 id="heading-summary">Summary</h4>
<p><strong>Remember</strong>:</p>
<ul>
<li><p>For SQL with dynamic values → <strong>Always use PreparedStatement</strong></p>
</li>
<li><p>For HTML with user input → <strong>Always escape HTML entities</strong></p>
</li>
<li><p>For safe contexts (emails, configs, logs) → <code>formatted()</code> is fine</p>
</li>
<li><p>Text blocks are for <strong>readability</strong>, not security</p>
</li>
</ul>
<h3 id="heading-when-to-use-text-blocks">When to Use Text Blocks</h3>
<p>✅ <strong>Text blocks are ideal for:</strong></p>
<p><strong>1. Embedded Languages and Markup</strong></p>
<ul>
<li><p>JSON, XML, HTML structures</p>
</li>
<li><p>SQL queries (especially complex ones with CTEs)</p>
</li>
<li><p>GraphQL, YAML configurations</p>
</li>
<li><p>SVG and other vector formats</p>
</li>
<li><p>Any literal multi-line code in other languages</p>
</li>
</ul>
<p><strong>2. Test Fixtures and Test Data</strong></p>
<ul>
<li><p>Mock JSON responses for API testing</p>
</li>
<li><p>Structured test data (CSV, XML, HTML)</p>
</li>
<li><p>Expected output validation in tests</p>
</li>
<li><p>Configuration templates</p>
</li>
</ul>
<p><strong>3. Documentation and Error Messages</strong></p>
<ul>
<li><p>Multi-line log messages with structure</p>
</li>
<li><p>Formatted error messages for users</p>
</li>
<li><p>Documentation templates</p>
</li>
<li><p>Code examples embedded as strings</p>
</li>
</ul>
<p><strong>4. Dynamic Content with</strong> <code>formatted()</code></p>
<ul>
<li><p>Parameterized SQL queries</p>
</li>
<li><p>Email templates with variables</p>
</li>
<li><p>API request bodies with dynamic fields</p>
</li>
<li><p>Configuration files with placeholders</p>
</li>
</ul>
<h3 id="heading-when-not-to-use-text-blocks">When NOT to Use Text Blocks</h3>
<p>❌ <strong>Avoid text blocks when:</strong></p>
<p><strong>1. Single-Line Strings</strong></p>
<pre><code class="lang-java"><span class="hljs-comment">// Don't do this</span>
String message = <span class="hljs-string">""</span><span class="hljs-string">"
    Hello World
    "</span><span class="hljs-string">""</span>;

<span class="hljs-comment">// Do this instead</span>
String message = <span class="hljs-string">"Hello World"</span>;
</code></pre>
<p><strong>2. Strings Requiring Platform-Specific Line Endings</strong></p>
<pre><code class="lang-java"><span class="hljs-comment">// Don't rely on text blocks for Windows-specific line endings</span>
<span class="hljs-comment">// Text blocks always normalize line terminators to \n (Unix style)</span>
<span class="hljs-comment">// If you need Windows-style \r\n (CRLF), convert explicitly:</span>
String textBlock = <span class="hljs-string">""</span><span class="hljs-string">"
    Line 1
    Line 2
    "</span><span class="hljs-string">""</span>;
String crlf = textBlock.replace(<span class="hljs-string">"\n"</span>, <span class="hljs-string">"\r\n"</span>);  <span class="hljs-comment">// Converts LF to CRLF</span>
</code></pre>
<p><strong>3. Complex String Processing</strong></p>
<p>If the string needs heavy regex or manipulation after creation, use regular strings for clarity:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Awkward: Text block with immediate complex processing</span>
String processed = <span class="hljs-string">""</span><span class="hljs-string">"
    user@example.com
    admin@example.com
    "</span><span class="hljs-string">""</span>.replaceAll(<span class="hljs-string">"[\\r\\n]+"</span>, <span class="hljs-string">","</span>).trim();
<span class="hljs-comment">// Result: "user@example.com,admin@example.com"</span>

<span class="hljs-comment">// Better: Regular string is clearer for processed data</span>
String[] emails = {<span class="hljs-string">"user@example.com"</span>, <span class="hljs-string">"admin@example.com"</span>};
String processed = String.join(<span class="hljs-string">","</span>, emails);
</code></pre>
<h3 id="heading-performance-considerations">Performance Considerations</h3>
<p>Text blocks have <strong>zero runtime overhead</strong> compared to traditional strings:</p>
<ul>
<li><p>Compiled to identical bytecode</p>
</li>
<li><p>No performance penalty vs. concatenation</p>
</li>
<li><p>String pooling works normally</p>
</li>
<li><p>Use freely without performance concerns</p>
</li>
</ul>
<h4 id="heading-compilation-performance">Compilation Performance</h4>
<p>Text block processing happens entirely at <strong>compile time</strong>, not runtime:</p>
<p><strong>Compile-Time Operations:</strong></p>
<ul>
<li><p>Indentation stripping is calculated when javac compiles the code</p>
</li>
<li><p>Line terminator normalization (converting <code>\r\n</code> to <code>\n</code>) happens during compilation</p>
</li>
<li><p>Escape sequence processing occurs at compile time</p>
</li>
<li><p>The resulting bytecode contains a plain String constant</p>
</li>
</ul>
<p><strong>Bytecode Equivalence:</strong></p>
<pre><code class="lang-java"><span class="hljs-comment">// Text block source code</span>
String block = <span class="hljs-string">""</span><span class="hljs-string">"
    Hello
    World
    "</span><span class="hljs-string">""</span>;

<span class="hljs-comment">// Compiles to identical bytecode as:</span>
String traditional = <span class="hljs-string">"Hello\nWorld\n"</span>;
</code></pre>
<p>The JVM sees no difference between text blocks and traditional strings—they produce the exact same bytecode. This means:</p>
<ul>
<li><p>No runtime CPU overhead for indentation calculations</p>
</li>
<li><p>No memory overhead for storing indentation metadata</p>
</li>
<li><p>No performance difference in String creation, comparison, or manipulation</p>
</li>
<li><p>JIT compiler optimizations apply identically to both forms</p>
</li>
</ul>
<p><strong>Practical Impact</strong>: You can use text blocks in performance-critical code (hot loops, high-throughput services) without any concerns. The readability benefit comes at zero runtime cost.</p>
<hr />
<h2 id="heading-text-blocks-vs-template-engines">Text Blocks vs Template Engines</h2>
<p>Understanding when to use text blocks versus full template engines (Thymeleaf, Freemarker, Velocity) is crucial for architecture decisions.</p>
<h3 id="heading-when-to-use-text-blocks-1">When to Use Text Blocks</h3>
<p><strong>Ideal for:</strong></p>
<ul>
<li><p><strong>Simple interpolation</strong>: Few variables, straightforward logic</p>
</li>
<li><p><strong>Build-time templates</strong>: Configuration files, code generation</p>
</li>
<li><p><strong>Test fixtures</strong>: Mock responses, expected output validation</p>
</li>
<li><p><strong>Small scope</strong>: Single-purpose, non-reusable templates</p>
</li>
<li><p><strong>No external files</strong>: Template lives alongside code</p>
</li>
</ul>
<p><strong>Example use cases:</strong></p>
<pre><code class="lang-java"><span class="hljs-comment">// Email notifications with 3-5 variables</span>
String email = <span class="hljs-string">""</span><span class="hljs-string">"
    Welcome, %s!
    Your order #%s has shipped.
    "</span><span class="hljs-string">""</span>.formatted(name, orderId);

<span class="hljs-comment">// API response templates</span>
String response = <span class="hljs-string">""</span><span class="hljs-string">"
    {"</span>status<span class="hljs-string">": "</span>%s<span class="hljs-string">", "</span>data<span class="hljs-string">": %s}
    "</span><span class="hljs-string">""</span>.formatted(status, jsonData);

<span class="hljs-comment">// Test expected output</span>
String expected = <span class="hljs-string">""</span><span class="hljs-string">"
    User: Alice
    Balance: $100.00
    "</span><span class="hljs-string">""</span>;
</code></pre>
<h3 id="heading-when-to-use-template-engines">When to Use Template Engines</h3>
<p><strong>Required for:</strong></p>
<ul>
<li><p><strong>Complex logic</strong>: Conditionals, loops, nested structures</p>
</li>
<li><p><strong>External templates</strong>: Designers/non-developers maintain templates</p>
</li>
<li><p><strong>Reusability</strong>: Same template used in multiple places</p>
</li>
<li><p><strong>Internationalization</strong>: Multi-language support with resource bundles</p>
</li>
<li><p><strong>Security features</strong>: Built-in HTML escaping, auto-sanitization</p>
</li>
<li><p><strong>Rich functionality</strong>: Includes, partials, macros, filters</p>
</li>
</ul>
<p><strong>Example use cases:</strong></p>
<pre><code class="lang-java"><span class="hljs-comment">// Thymeleaf for complex HTML with logic</span>
&lt;div th:<span class="hljs-keyword">if</span>=<span class="hljs-string">"${user.premium}"</span>&gt;
    &lt;h2&gt;Welcome, Premium Member!&lt;/h2&gt;
    &lt;ul&gt;
        &lt;li th:each=<span class="hljs-string">"feature : ${premiumFeatures}"</span> th:text=<span class="hljs-string">"${feature}"</span>&gt;&lt;/li&gt;
    &lt;/ul&gt;
&lt;/div&gt;

<span class="hljs-comment">// Freemarker for dynamic content generation</span>
&lt;#list products as product&gt;
    &lt;#<span class="hljs-keyword">if</span> product.stock &gt; <span class="hljs-number">0</span>&gt;
        ${product.name}: $${product.price}
    &lt;/#<span class="hljs-keyword">if</span>&gt;
&lt;/#list&gt;
</code></pre>
<hr />
<h2 id="heading-resources">Resources</h2>
<h3 id="heading-official-documentation">Official Documentation</h3>
<ul>
<li><p><strong>JEP 378 - Text Blocks</strong>: <a target="_blank" href="https://openjdk.org/jeps/378">openjdk.org/jeps/378</a> The official Java Enhancement Proposal introducing text blocks in Java 15. Includes design rationale, indentation rules, and escape sequence specifications.</p>
</li>
<li><p><strong>JEP 359 - Record Classes (Preview)</strong>: <a target="_blank" href="https://openjdk.org/jeps/359">openjdk.org/jeps/359</a> First preview in Java 14 showing the evolution of text blocks across preview stages.</p>
</li>
<li><p><strong>Java Language Specification - Text Blocks</strong>: <a target="_blank" href="https://docs.oracle.com/javase/specs/jls/se17/html/jls-3.html#jls-3.3">docs.oracle.com/javase/specs/jls/se17/html/jls-3.html#jls-3.3</a> Formal specification of text block syntax and semantics in the Java Language Specification.</p>
</li>
<li><p><strong>Java Platform String Documentation</strong>: <a target="_blank" href="https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html">docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/String.html</a> Complete API documentation including the <code>formatted()</code> method for string interpolation.</p>
</li>
</ul>
<h3 id="heading-interactive-references">Interactive References</h3>
<ul>
<li><p><strong>Java Almanac - Text Blocks</strong>: <a target="_blank" href="https://javaalmanac.io/features/text-blocks/">javaalmanac.io/features/text-blocks/</a> Interactive examples with runnable code demonstrating text block syntax and patterns.</p>
</li>
<li><p><strong>Java Tutorials - Text Blocks</strong>: <a target="_blank" href="https://docs.oracle.com/javase/tutorial/java/data/textblocks.html">docs.oracle.com/javase/tutorial/java/data/textblocks.html</a> Official Oracle tutorial covering text block basics and common use cases.</p>
</li>
</ul>
<h3 id="heading-code-repository">Code Repository</h3>
<ul>
<li><p><strong>GitHub Repository - blog-9mac-dev-code</strong>: <a target="_blank" href="https://github.com/dawid-swist/blog-9mac-dev-code">github.com/dawid-swist/blog-9mac-dev-code</a> All examples from this article with complete JUnit test suite:</p>
<pre><code class="lang-bash">  git <span class="hljs-built_in">clone</span> https://github.com/dawid-swist/blog-9mac-dev-code.git
  <span class="hljs-built_in">cd</span> blog-post-examples/java/2025-10-25-java17-features-every-senior-developer-should-know
  ../../gradlew <span class="hljs-built_in">test</span> --tests *part5*
</code></pre>
</li>
</ul>
<h3 id="heading-additional-reading">Additional Reading</h3>
<ul>
<li><p><strong>Brian Goetz - State of the Specification</strong>: Java language architect's updates on evolution</p>
</li>
<li><p><strong>Inside Java Podcast - Text Blocks Episodes</strong>: Design decisions and implementation details</p>
</li>
<li><p><strong>Effective Java (3rd Edition)</strong>: Consider using text blocks for clarity when maintaining embedded language strings</p>
</li>
</ul>
<hr />
<h2 id="heading-summary-1">Summary.</h2>
<h3 id="heading-implementation-checklist-for-your-code">Implementation Checklist for Your Code</h3>
<p>When migrating to text blocks:</p>
<ul>
<li><p>✅ Identify multi-line string literals (JSON, SQL, HTML, XML)</p>
</li>
<li><p>✅ Replace concatenation with triple-quote delimiters</p>
</li>
<li><p>✅ Verify indentation is correct (closing <code>"""</code> determines strip level)</p>
</li>
<li><p>✅ Remove unnecessary escape sequences (<code>\"</code> becomes <code>"</code>)</p>
</li>
<li><p>✅ Use <code>formatted()</code> for parameterized strings instead of multiple calls</p>
</li>
<li><p>✅ Test edge cases (empty lines, trailing whitespace, special characters)</p>
</li>
<li><p>✅ Update test fixtures to use text blocks for readability</p>
</li>
</ul>
<h3 id="heading-common-mistakes-to-avoid">Common Mistakes to Avoid</h3>
<ol>
<li><p><strong>Forgetting newline after opening</strong> <code>"""</code></p>
<pre><code class="lang-java"> <span class="hljs-comment">// Wrong - content on same line as opening delimiter</span>
 String block = <span class="hljs-string">""</span><span class="hljs-string">"Hello"</span><span class="hljs-string">""</span>;

 <span class="hljs-comment">// Correct - newline after opening delimiter</span>
 String block = <span class="hljs-string">""</span><span class="hljs-string">"
     Hello"</span><span class="hljs-string">""</span>;
</code></pre>
</li>
<li><p><strong>Misunderstanding indent stripping</strong></p>
<pre><code class="lang-java"> <span class="hljs-comment">// This strips 4 spaces (closing """ is indented at 4 spaces)</span>
 String block = <span class="hljs-string">""</span><span class="hljs-string">"
     Content"</span><span class="hljs-string">""</span>;  <span class="hljs-comment">// Result: "Content\n"</span>

 <span class="hljs-comment">// This strips 0 spaces (closing """ at left margin, content not indented)</span>
 String block = <span class="hljs-string">""</span><span class="hljs-string">"
 Content"</span><span class="hljs-string">""</span>;  <span class="hljs-comment">// Result: "Content\n"</span>

 <span class="hljs-comment">// This doesn't strip anything (closing """ at left margin, content indented)</span>
 String block = <span class="hljs-string">""</span><span class="hljs-string">"
     Content
 "</span><span class="hljs-string">""</span>;  <span class="hljs-comment">// Result: "    Content\n"</span>
</code></pre>
</li>
<li><p><strong>Forgetting that trailing whitespace is removed</strong></p>
<pre><code class="lang-java"> <span class="hljs-comment">// Trailing spaces after "text" are removed</span>
 String block = <span class="hljs-string">""</span><span class="hljs-string">"
     text   \
     more
     "</span><span class="hljs-string">""</span>;  <span class="hljs-comment">// Use \s if you need trailing space</span>
</code></pre>
</li>
<li><p><strong>Using</strong> <code>formatted()</code> with SQL (SQL Injection Risk)</p>
<pre><code class="lang-java"> <span class="hljs-comment">// DANGEROUS: This is vulnerable to SQL injection!</span>
 String sql = <span class="hljs-string">""</span><span class="hljs-string">"
     SELECT * FROM table WHERE name = '%s'
     "</span><span class="hljs-string">""</span>.formatted(name);

 <span class="hljs-comment">// SAFE: Always use PreparedStatement with parameterized queries</span>
 String query = <span class="hljs-string">""</span><span class="hljs-string">"
     SELECT * FROM table WHERE name = ?
     "</span><span class="hljs-string">""</span>;
 PreparedStatement ps = connection.prepareStatement(query);
 ps.setString(<span class="hljs-number">1</span>, name);
</code></pre>
</li>
</ol>
<hr />
<p><em>Written for</em> <a target="_blank" href="https://blog.9mac.dev"><em>blog.9mac.dev</em></a><em>Part of the "Java 17 Features Every Senior Developer Should Know" series</em></p>
<p><strong>Previous</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-4-pattern-matching-and-switch-expressions">Part 4 - Pattern Matching &amp; Switch Expressions</a> <strong>Next</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-6-syntax-cheat-sheet-and-reference-guide?showSharer=true">Part 6 - Summary with Syntax Cheat Sheet</a></p>
]]></content:encoded></item><item><title><![CDATA[Java 17 Features Every Senior Developer Should Know - Part 4: Pattern Matching & Switch Expressions]]></title><description><![CDATA[Welcome to Part 4 of our comprehensive series on Java 17 features! In previous installments, we explored the var keyword for type inference and Records for eliminating boilerplate in data classes. Today, we're diving into two transformative features ...]]></description><link>https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-4-pattern-matching-and-switch-expressions</link><guid isPermaLink="true">https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-4-pattern-matching-and-switch-expressions</guid><category><![CDATA[Java]]></category><category><![CDATA[java 17]]></category><dc:creator><![CDATA[Dawid Świst]]></dc:creator><pubDate>Sun, 16 Nov 2025 19:30:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1763364836287/50c42914-fb9a-4087-8144-ac264eea06dc.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome to Part 4 of our comprehensive series on Java 17 features! In previous installments, we explored the <code>var</code> keyword for type inference and Records for eliminating boilerplate in data classes. Today, we're diving into two transformative features that fundamentally change how we write conditional logic and type checks in Java: <strong>Pattern Matching for instanceof</strong> (JEP 394) and <strong>Switch Expressions</strong> (JEP 361).</p>
<p>These features represent Java's first serious steps toward pattern matching—a programming paradigm that functional languages have enjoyed for decades. For developers upgrading from Java 8, this is where modern Java starts to feel truly different.</p>
<hr />
<h2 id="heading-what-is-pattern-matching">What is Pattern Matching?</h2>
<p><strong>Pattern matching</strong> is a general programming concept—not specific to Java—for checking a value against a pattern and, if it matches, deconstructing that value into its constituent parts. Java introduces pattern matching incrementally across multiple language features.</p>
<p>In academic terms, pattern matching is a form of <strong>structural decomposition</strong> combined with <strong>conditional dispatch</strong>. You're simultaneously asking "what shape is this data?" and "if it matches, bind the components to names I can use."</p>
<h3 id="heading-pattern-matching-in-java-multiple-forms">Pattern Matching in Java: Multiple Forms</h3>
<p>Java's approach to pattern matching is incremental—different patterns are added in different versions:</p>
<ol>
<li><p><strong>Type patterns</strong> (Java 16+): <code>instanceof Circle c</code> - match and cast types</p>
</li>
<li><p><strong>Guarded patterns</strong> (Java 16+): Add conditions with <code>when</code> clause: <code>instanceof String s when s.length() &gt; 0</code></p>
</li>
<li><p><strong>Record patterns</strong> (Java 17+): Destructure records - <code>case Point(int x, int y) -&gt;</code></p>
</li>
<li><p><strong>Array patterns</strong> (Preview, future): Match array contents - coming in future versions</p>
</li>
</ol>
<p>Each form can appear in different contexts:</p>
<ul>
<li><p><strong>instanceof expressions</strong> (Java 16+): Type checking with automatic casting</p>
</li>
<li><p><strong>switch statements/expressions</strong> (Java 14+ with patterns in Java 17+)</p>
</li>
</ul>
<h3 id="heading-the-problem-pattern-matching-solves">The Problem Pattern Matching Solves</h3>
<p>Traditional imperative programming requires multiple steps to work with polymorphic data:</p>
<ol>
<li><p>Check the type using <code>instanceof</code></p>
</li>
<li><p>Cast to that type manually</p>
</li>
<li><p>Extract components (for records)</p>
</li>
<li><p>Repeat for each possible type</p>
</li>
</ol>
<p>Pattern matching collapses these steps into declarative syntax that's both safer and more concise. Instead of procedural "how to check," you write declarative "what to match." This represents a <strong>paradigm shift</strong> that:</p>
<ul>
<li><p><strong>Reduces boilerplate</strong>: Eliminates manual type casting and null checks</p>
</li>
<li><p><strong>Improves safety</strong>: Compiler enforces exhaustiveness and correct variable scopes</p>
</li>
<li><p><strong>Reduces errors</strong>: Prevents entire classes of type-casting and null-pointer bugs</p>
</li>
<li><p><strong>Clarifies intent</strong>: Makes code more readable by expressing "what we're matching for" rather than "how to check"</p>
</li>
</ul>
<hr />
<h2 id="heading-historical-context-java-1-8">Historical Context: Java 1-8</h2>
<p>To appreciate pattern matching, we need to understand the pain points it solves. Let's examine how Java developers handled type checking and conditional logic before Java 14-16.</p>
<h3 id="heading-the-instanceof-cast-ceremony">The instanceof-cast Ceremony</h3>
<p>Before pattern matching, checking types required explicit casting—a source of verbosity and potential <code>ClassCastException</code> errors:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Pre-Java 16: The instanceof-cast pattern</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">describe</span><span class="hljs-params">(Object obj)</span> </span>{
    <span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> String) {
        String s = (String) obj;  <span class="hljs-comment">// Manual cast required</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">"String of length "</span> + s.length();
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> Integer) {
        Integer i = (Integer) obj;  <span class="hljs-comment">// Repeated pattern</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Integer: "</span> + i;
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Unknown type"</span>;
    }
}
</code></pre>
<p><strong>Problems with this approach:</strong></p>
<ul>
<li><p><strong>Redundancy</strong>: We write <code>String</code> three times in a single branch</p>
</li>
<li><p><strong>Error-prone</strong>: Nothing prevents casting to the wrong type</p>
</li>
<li><p><strong>Verbose</strong>: The pattern obscures the business logic</p>
</li>
<li><p><strong>Refactoring friction</strong>: Changing types means updating multiple lines</p>
</li>
</ul>
<p>This "test-and-cast" idiom was so common that IDEs created templates for it, but the fundamental problem remained: the language didn't support what we were trying to express.</p>
<h3 id="heading-the-classic-switch-statement">The Classic Switch Statement</h3>
<p>Traditional switch statements had their own set of issues:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Pre-Java 14: Classic switch with fall-through</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getDaysInMonth</span><span class="hljs-params">(String month)</span> </span>{
    <span class="hljs-keyword">int</span> days;
    <span class="hljs-keyword">switch</span> (month) {
        <span class="hljs-keyword">case</span> <span class="hljs-string">"January"</span>:
        <span class="hljs-keyword">case</span> <span class="hljs-string">"March"</span>:
        <span class="hljs-keyword">case</span> <span class="hljs-string">"May"</span>:
        <span class="hljs-keyword">case</span> <span class="hljs-string">"July"</span>:
        <span class="hljs-keyword">case</span> <span class="hljs-string">"August"</span>:
        <span class="hljs-keyword">case</span> <span class="hljs-string">"October"</span>:
        <span class="hljs-keyword">case</span> <span class="hljs-string">"December"</span>:
            days = <span class="hljs-number">31</span>;
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">"February"</span>:
            days = <span class="hljs-number">28</span>;
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> <span class="hljs-string">"April"</span>:
        <span class="hljs-keyword">case</span> <span class="hljs-string">"June"</span>:
        <span class="hljs-keyword">case</span> <span class="hljs-string">"September"</span>:
        <span class="hljs-keyword">case</span> <span class="hljs-string">"November"</span>:
            days = <span class="hljs-number">30</span>;
            <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">default</span>:
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Invalid month"</span>);
    }
    <span class="hljs-keyword">return</span> days;
}
</code></pre>
<p><strong>Problems:</strong></p>
<ul>
<li><p><strong>Fall-through behavior</strong>: Forgetting <code>break</code> causes bugs (the infamous "fall-through" error)</p>
</li>
<li><p><strong>Not an expression</strong>: Can't assign switch results directly to variables</p>
</li>
<li><p><strong>Verbose grouping</strong>: Multiple cases cascade without clear grouping syntax</p>
</li>
<li><p><strong>Mutation required</strong>: Need to declare variable before switch, assign inside</p>
</li>
<li><p><strong>No exhaustiveness checking</strong>: Compiler won't warn about missing cases (except for enums with <code>-Xlint</code>)</p>
</li>
</ul>
<p>The <code>-Xlint:fallthrough</code> warning existed, but it was a band-aid on a fundamental design flaw.</p>
<h3 id="heading-equals-method-boilerplate">Equals Method Boilerplate</h3>
<p>The <code>equals()</code> method implementation was particularly painful:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Pre-Java 16: Traditional equals() implementation</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Point</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> x, y;

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">equals</span><span class="hljs-params">(Object obj)</span> </span>{
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span> == obj) <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
        <span class="hljs-keyword">if</span> (obj == <span class="hljs-keyword">null</span>) <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
        <span class="hljs-keyword">if</span> (getClass() != obj.getClass()) <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;

        Point other = (Point) obj;  <span class="hljs-comment">// Manual cast after type check</span>
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.x == other.x &amp;&amp; <span class="hljs-keyword">this</span>.y == other.y;
    }
}
</code></pre>
<p>The pattern is mechanical, but it requires four lines just to get to the actual comparison logic.</p>
<p>Java 16+ introduced <strong>pattern matching</strong> to eliminate the test-and-cast ceremony, while Java 14+ made <strong>switch expressions</strong> return values and prevent fall-through bugs. Java 17 brings these together: pattern matching in switch statements creates powerful, concise conditional logic.</p>
<hr />
<h2 id="heading-changes-to-instanceof-java-16">Changes to instanceof (Java 16+)</h2>
<p>Java 16 introduced major changes to the <code>instanceof</code> operator via <strong>Pattern Matching for instanceof</strong> (JEP 394). In the Java Language Specification, this feature is referred to as <strong>"pattern variables"</strong> or <strong>"type patterns in instanceof"</strong>.</p>
<p>These changes finally bring to Java a feature that other languages have had for decades. Swift, Scala, and functional languages like Haskell pioneered pattern matching as a way to combine type checking, casting, and value extraction into a single expression.</p>
<p>Java's conservative approach meant pattern matching arrived later, but it's now transforming how we write conditional logic, eliminating the test-and-cast ceremony that plagued Java for decades.</p>
<h3 id="heading-basic-syntax">Basic Syntax</h3>
<p>The new syntax combines type checking and casting in one operation:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Java 16+: Pattern matching for instanceof</span>
<span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> String s) {
    <span class="hljs-comment">// 's' is in scope here, already cast to String</span>
    System.out.println(<span class="hljs-string">"Length: "</span> + s.length());
}

<span class="hljs-comment">// Before Java 16 - required manual cast</span>
<span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> String) {
    String s = (String) obj;
    System.out.println(<span class="hljs-string">"Length: "</span> + s.length());
}
</code></pre>
<p>The pattern variable <code>s</code> is automatically cast and scoped to where the type check succeeds.</p>
<h3 id="heading-scope-rules">Scope Rules</h3>
<p>The pattern variable is only in scope where the compiler can <strong>prove it was assigned</strong>.</p>
<h4 id="heading-how-pattern-variables-are-scoped">How Pattern Variables Are Scoped</h4>
<p><strong>Rule 1: After</strong> <code>instanceof</code> in same if-block</p>
<pre><code class="lang-java"><span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> String s) {
    <span class="hljs-comment">// ✓ 's' is a String here</span>
    System.out.println(s.length());
}
</code></pre>
<p><strong>Rule 2: With</strong> <code>&amp;&amp;</code> (AND) on the right side</p>
<pre><code class="lang-java"><span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> String s &amp;&amp; s.length() &gt; <span class="hljs-number">5</span>) {
    <span class="hljs-comment">// ✓ 's' is a String AND length &gt; 5 here</span>
    System.out.println(s.toUpperCase());
}
</code></pre>
<p><strong>Rule 3: NOT with</strong> <code>||</code> (OR) on the right side</p>
<pre><code class="lang-java"><span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> String s || isTrusted(obj)) {
    <span class="hljs-comment">// ✗ DOES NOT COMPILE</span>
    <span class="hljs-comment">// 's' might not exist if isTrusted() was true instead</span>
    System.out.println(s.length());
}

<span class="hljs-comment">// ✓ CORRECT: Use separate if statements</span>
<span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> String s) {
    System.out.println(s.length());
}
<span class="hljs-keyword">if</span> (isTrusted(obj)) {
    System.out.println(<span class="hljs-string">"Trusted"</span>);
}
</code></pre>
<p><strong>Rule 4: In else block with negation</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">if</span> (!(obj <span class="hljs-keyword">instanceof</span> String s)) {
    <span class="hljs-comment">// 's' is NOT in scope here (obj is not a String)</span>
} <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">// ✓ 's' IS in scope here (obj is definitely a String)</span>
    System.out.println(s.length());
}
</code></pre>
<h3 id="heading-null-handling">Null Handling</h3>
<p>Pattern matching handles null naturally—instanceof already returns false for null:</p>
<pre><code class="lang-java">Object obj = <span class="hljs-keyword">null</span>;
<span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> String s) {
    <span class="hljs-comment">// This block never executes for null</span>
}
</code></pre>
<p>This is safer than manual casting, which required explicit null checks.</p>
<h3 id="heading-real-world-benefits">Real-World Benefits</h3>
<p>Pattern matching shines in polymorphic code:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Before: Verbose hierarchy traversal</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">processShape</span><span class="hljs-params">(Shape shape)</span> </span>{
    <span class="hljs-keyword">if</span> (shape <span class="hljs-keyword">instanceof</span> Circle) {
        Circle c = (Circle) shape;
        System.out.println(<span class="hljs-string">"Circle radius: "</span> + c.radius());
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (shape <span class="hljs-keyword">instanceof</span> Rectangle) {
        Rectangle r = (Rectangle) shape;
        System.out.println(<span class="hljs-string">"Rectangle: "</span> + r.width() + <span class="hljs-string">"x"</span> + r.height());
    }
}

<span class="hljs-comment">// After: Concise and clear</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">processShape</span><span class="hljs-params">(Shape shape)</span> </span>{
    <span class="hljs-keyword">if</span> (shape <span class="hljs-keyword">instanceof</span> Circle c) {
        System.out.println(<span class="hljs-string">"Circle radius: "</span> + c.radius());
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (shape <span class="hljs-keyword">instanceof</span> Rectangle r) {
        System.out.println(<span class="hljs-string">"Rectangle: "</span> + r.width() + <span class="hljs-string">"x"</span> + r.height());
    }
}
</code></pre>
<hr />
<h2 id="heading-switch-expressions-java-14">Switch Expressions (Java 14+)</h2>
<p>Java 14 introduced <strong>switch expressions</strong> (JEP 361), transforming switch from a statement to an expression that returns values.</p>
<h3 id="heading-arrow-syntax-gt">Arrow Syntax (<code>-&gt;</code>)</h3>
<p>The new arrow syntax eliminates fall-through:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Java 14+: Switch expression with arrows</span>
String dayType = <span class="hljs-keyword">switch</span> (day) {
    <span class="hljs-keyword">case</span> MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -&gt; <span class="hljs-string">"Weekday"</span>;
    <span class="hljs-keyword">case</span> SATURDAY, SUNDAY -&gt; <span class="hljs-string">"Weekend"</span>;
};

<span class="hljs-comment">// Before Java 14: Switch statement</span>
String dayType;
<span class="hljs-keyword">switch</span> (day) {
    <span class="hljs-keyword">case</span> MONDAY:
    <span class="hljs-keyword">case</span> TUESDAY:
    <span class="hljs-keyword">case</span> WEDNESDAY:
    <span class="hljs-keyword">case</span> THURSDAY:
    <span class="hljs-keyword">case</span> FRIDAY:
        dayType = <span class="hljs-string">"Weekday"</span>;
        <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> SATURDAY:
    <span class="hljs-keyword">case</span> SUNDAY:
        dayType = <span class="hljs-string">"Weekend"</span>;
        <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">default</span>:
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException();
}
</code></pre>
<p><strong>Key differences:</strong></p>
<ul>
<li><p><strong>No fall-through</strong>: Each case executes exactly its branch</p>
</li>
<li><p><strong>Expression</strong>: Returns a value directly</p>
</li>
<li><p><strong>Concise</strong>: Multiple labels separated by commas</p>
</li>
<li><p><strong>No break needed</strong>: Implicit at end of each branch</p>
</li>
</ul>
<h3 id="heading-the-yield-keyword">The yield Keyword</h3>
<p>For multi-statement branches, use <code>yield</code> to return a value:</p>
<pre><code class="lang-java"><span class="hljs-keyword">int</span> result = <span class="hljs-keyword">switch</span> (operator) {
    <span class="hljs-keyword">case</span> <span class="hljs-string">"+"</span> -&gt; {
        System.out.println(<span class="hljs-string">"Adding"</span>);
        yield a + b;
    }
    <span class="hljs-keyword">case</span> <span class="hljs-string">"-"</span> -&gt; {
        System.out.println(<span class="hljs-string">"Subtracting"</span>);
        yield a - b;
    }
    <span class="hljs-keyword">default</span> -&gt; <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException();
};
</code></pre>
<p><code>yield</code> is like <code>return</code> for switch expressions.</p>
<h3 id="heading-exhaustiveness">Exhaustiveness</h3>
<p>Switch expressions must be <strong>exhaustive</strong>—they must handle all possible values:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Enum switch: Must cover all values OR include default</span>
<span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Color</span> </span>{ RED, GREEN, BLUE }

String name = <span class="hljs-keyword">switch</span> (color) {
    <span class="hljs-keyword">case</span> RED -&gt; <span class="hljs-string">"Red"</span>;
    <span class="hljs-keyword">case</span> GREEN -&gt; <span class="hljs-string">"Green"</span>;
    <span class="hljs-keyword">case</span> BLUE -&gt; <span class="hljs-string">"Blue"</span>;
    <span class="hljs-comment">// No default needed - all enum values covered</span>
};

<span class="hljs-comment">// Primitives and strings: Must include default</span>
String describe = <span class="hljs-keyword">switch</span> (num) {
    <span class="hljs-keyword">case</span> <span class="hljs-number">0</span> -&gt; <span class="hljs-string">"Zero"</span>;
    <span class="hljs-keyword">case</span> <span class="hljs-number">1</span> -&gt; <span class="hljs-string">"One"</span>;
    <span class="hljs-keyword">default</span> -&gt; <span class="hljs-string">"Other"</span>;  <span class="hljs-comment">// Required for non-enum types</span>
};
</code></pre>
<p>The compiler enforces exhaustiveness, preventing bugs from missing cases.</p>
<h3 id="heading-four-forms-of-switch">Four Forms of Switch</h3>
<p>Java now has four switch variants:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Type</td><td>Arrow (<code>-&gt;</code>)</td><td>Colon (<code>:</code>)</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Expression</strong></td><td><code>int x = switch(v) { case 1 -&gt; 10; }</code></td><td><code>int x = switch(v) { case 1: yield 10; }</code></td></tr>
<tr>
<td><strong>Statement</strong></td><td><code>switch(v) { case 1 -&gt; x = 10; }</code></td><td><code>switch(v) { case 1: x = 10; break; }</code></td></tr>
</tbody>
</table>
</div><p><strong>Best practice</strong>: Prefer arrow syntax for new code. Use colon syntax only when you need fall-through (rare).</p>
<hr />
<h2 id="heading-pattern-matching-in-switch-java-17">Pattern Matching in Switch (Java 17+)</h2>
<p>Java 17 brought <strong>pattern matching in switch expressions</strong> (JEP 406), combining everything you've learned so far into one powerful feature. Instead of using <code>instanceof</code> checks for pattern matching, you can now directly match patterns inside switch expressions.</p>
<h3 id="heading-type-patterns-in-switch">Type Patterns in Switch</h3>
<p>Match different types directly without <code>instanceof</code>:</p>
<pre><code class="lang-java">String result = <span class="hljs-keyword">switch</span> (obj) {
    <span class="hljs-keyword">case</span> String s -&gt; <span class="hljs-string">"String: "</span> + s;
    <span class="hljs-keyword">case</span> Integer i -&gt; <span class="hljs-string">"Integer: "</span> + i;
    <span class="hljs-keyword">case</span> Double d -&gt; <span class="hljs-string">"Double: "</span> + d;
    <span class="hljs-keyword">case</span> <span class="hljs-keyword">null</span> -&gt; <span class="hljs-string">"Null value"</span>;
    <span class="hljs-keyword">default</span> -&gt; <span class="hljs-string">"Unknown type"</span>;
};
</code></pre>
<h3 id="heading-guarded-patterns">Guarded Patterns</h3>
<p>Add conditions to patterns using the <code>when</code> clause:</p>
<pre><code class="lang-java">String classify = <span class="hljs-keyword">switch</span> (obj) {
    <span class="hljs-keyword">case</span> String s when s.isEmpty() -&gt; <span class="hljs-string">"Empty string"</span>;
    <span class="hljs-keyword">case</span> String s when s.length() &lt; <span class="hljs-number">5</span> -&gt; <span class="hljs-string">"Short string: "</span> + s;
    <span class="hljs-keyword">case</span> String s -&gt; <span class="hljs-string">"Long string: "</span> + s;
    <span class="hljs-keyword">default</span> -&gt; <span class="hljs-string">"Not a string"</span>;
};
</code></pre>
<h3 id="heading-sealed-types-with-pattern-matching">Sealed Types with Pattern Matching</h3>
<p>Sealed types + pattern matching = exhaustiveness guaranteed by the compiler:</p>
<pre><code class="lang-java">sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Shape</span> <span class="hljs-title">permits</span> <span class="hljs-title">Circle</span>, <span class="hljs-title">Rectangle</span>, <span class="hljs-title">Triangle</span> </span>{}
<span class="hljs-function">record <span class="hljs-title">Circle</span><span class="hljs-params">(<span class="hljs-keyword">double</span> radius)</span> implements Shape </span>{}
<span class="hljs-function">record <span class="hljs-title">Rectangle</span><span class="hljs-params">(<span class="hljs-keyword">int</span> width, <span class="hljs-keyword">int</span> height)</span> implements Shape </span>{}
<span class="hljs-function">record <span class="hljs-title">Triangle</span><span class="hljs-params">(<span class="hljs-keyword">int</span> base, <span class="hljs-keyword">int</span> height)</span> implements Shape </span>{}

<span class="hljs-keyword">int</span> area = <span class="hljs-keyword">switch</span> (shape) {
    <span class="hljs-keyword">case</span> Circle c -&gt; (<span class="hljs-keyword">int</span>) (Math.PI * c.radius() * c.radius());
    <span class="hljs-keyword">case</span> Rectangle r -&gt; r.width() * r.height();
    <span class="hljs-keyword">case</span> Triangle t -&gt; (t.base() * t.height()) / <span class="hljs-number">2</span>;
    <span class="hljs-comment">// No default needed - compiler enforces all cases</span>
};
</code></pre>
<h3 id="heading-record-patterns">Record Patterns</h3>
<p>Destructure records directly in switch patterns:</p>
<pre><code class="lang-java"><span class="hljs-function">record <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{}

String location = <span class="hljs-keyword">switch</span> (obj) {
    <span class="hljs-function"><span class="hljs-keyword">case</span> <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> when x &gt; 0 &amp;&amp; y &gt; 0 -&gt; "Quadrant 1"</span>;
    <span class="hljs-function"><span class="hljs-keyword">case</span> <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> when x &lt; 0 &amp;&amp; y &gt; 0 -&gt; "Quadrant 2"</span>;
    <span class="hljs-function"><span class="hljs-keyword">case</span> <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> when x </span>== <span class="hljs-number">0</span> &amp;&amp; y == <span class="hljs-number">0</span> -&gt; <span class="hljs-string">"Origin"</span>;
    <span class="hljs-keyword">default</span> -&gt; <span class="hljs-string">"Other"</span>;
};
</code></pre>
<hr />
<h2 id="heading-practical-examples">Practical Examples</h2>
<p>Let's explore pattern matching and switch expressions through seven comprehensive examples.</p>
<h3 id="heading-example-1-basic-pattern-matching-with-instanceof">Example 1: Basic Pattern Matching with instanceof</h3>
<p>Pattern matching for instanceof eliminates redundant casting and makes type checks more concise.</p>
<p><strong>Before (Java 8-15):</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> String) {
    String s = (String) obj;
    <span class="hljs-keyword">return</span> <span class="hljs-string">"String of length "</span> + s.length();
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> Integer) {
    Integer i = (Integer) obj;
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Integer: "</span> + i;
}
</code></pre>
<p><strong>After (Java 16+):</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> String s) {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"String of length "</span> + s.length();
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> Integer i) {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Integer: "</span> + i;
}
</code></pre>
<p><strong>Full Example:</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BasicPatternMatchingExample</span> </span>{

    <span class="hljs-comment">// Java 16+ pattern matching</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">describeModern</span><span class="hljs-params">(Object obj)</span> </span>{
        <span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> String s) {
            <span class="hljs-keyword">return</span> <span class="hljs-string">"String of length "</span> + s.length();
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> Integer i) {
            <span class="hljs-keyword">return</span> <span class="hljs-string">"Integer: "</span> + i;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> Double d) {
            <span class="hljs-keyword">return</span> <span class="hljs-string">"Double: "</span> + d;
        }
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Unknown type"</span>;
    }

    <span class="hljs-comment">// Demonstrating scope rules with &amp;&amp; (AND) and negation</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">demonstrateScope</span><span class="hljs-params">(Object obj)</span> </span>{
        <span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> String s &amp;&amp; s.length() &gt; <span class="hljs-number">5</span>) {
            <span class="hljs-keyword">return</span> <span class="hljs-string">"Long string: "</span> + s.toUpperCase();
        }
        <span class="hljs-keyword">if</span> (!(obj <span class="hljs-keyword">instanceof</span> String s)) {
            <span class="hljs-keyword">return</span> <span class="hljs-string">"Not a string"</span>;
        }
        <span class="hljs-keyword">return</span> <span class="hljs-string">"String in else: "</span> + s;
    }
}
</code></pre>
<p><strong>Test:</strong></p>
<pre><code class="lang-java"><span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldUsePatternMatching</span><span class="hljs-params">()</span> </span>{
    assertEquals(<span class="hljs-string">"String of length 5"</span>,
        BasicPatternMatchingExample.describeModern(<span class="hljs-string">"Hello"</span>));
}
</code></pre>
<p><strong>Key Insight</strong>: Pattern matching combines type checking, casting, and variable binding in a single operation. The scope rules ensure pattern variables only exist where the type check succeeds—in the if-block, with <code>&amp;&amp;</code> operators on the right side, or in negation blocks.</p>
<hr />
<h3 id="heading-example-2-switch-expressions-with-enums">Example 2: Switch Expressions with Enums</h3>
<p>Switch expressions with enums demonstrate exhaustiveness checking—the compiler ensures all enum values are handled.</p>
<p><strong>Before (Java 8-13):</strong></p>
<pre><code class="lang-java">String result;
<span class="hljs-keyword">switch</span> (day) {
    <span class="hljs-keyword">case</span> MONDAY:
    <span class="hljs-keyword">case</span> TUESDAY:
        result = <span class="hljs-string">"Weekday"</span>;
        <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> SATURDAY:
    <span class="hljs-keyword">case</span> SUNDAY:
        result = <span class="hljs-string">"Weekend"</span>;
        <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">default</span>:
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException();
}
</code></pre>
<p><strong>After (Java 14+):</strong></p>
<pre><code class="lang-java">String result = <span class="hljs-keyword">switch</span> (day) {
    <span class="hljs-keyword">case</span> MONDAY, TUESDAY -&gt; <span class="hljs-string">"Weekday"</span>;
    <span class="hljs-keyword">case</span> SATURDAY, SUNDAY -&gt; <span class="hljs-string">"Weekend"</span>;
};
</code></pre>
<p><strong>Full Example:</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SwitchEnumExample</span> </span>{
    <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Day</span> </span>{ MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }

    <span class="hljs-comment">// Modern switch expression with grouped cases</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getDayType</span><span class="hljs-params">(Day day)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">switch</span> (day) {
            <span class="hljs-keyword">case</span> MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -&gt; <span class="hljs-string">"Weekday"</span>;
            <span class="hljs-keyword">case</span> SATURDAY, SUNDAY -&gt; <span class="hljs-string">"Weekend"</span>;
        };
    }

    <span class="hljs-comment">// No default needed - all enum values covered by compiler</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getSeasonMonths</span><span class="hljs-params">(String season)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">switch</span> (season) {
            <span class="hljs-keyword">case</span> <span class="hljs-string">"SPRING"</span> -&gt; <span class="hljs-number">3</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-string">"SUMMER"</span> -&gt; <span class="hljs-number">3</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-string">"FALL"</span> -&gt; <span class="hljs-number">3</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-string">"WINTER"</span> -&gt; <span class="hljs-number">3</span>;
        };
    }
}
</code></pre>
<p><strong>Test:</strong></p>
<pre><code class="lang-java"><span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldClassifyDaysWithSwitch</span><span class="hljs-params">()</span> </span>{
    assertEquals(<span class="hljs-string">"Weekday"</span>, SwitchEnumExample.getDayType(Day.MONDAY));
    assertEquals(<span class="hljs-string">"Weekend"</span>, SwitchEnumExample.getDayType(Day.SATURDAY));
}
</code></pre>
<p><strong>Key Insight</strong>: Switch expressions with enums benefit from exhaustiveness checking. When all enum values are covered, no <code>default</code> case is needed. The compiler will error if a new enum value is added without updating the switch.</p>
<hr />
<h3 id="heading-example-3-switch-expressions-with-yield">Example 3: Switch Expressions with yield</h3>
<p>The <code>yield</code> keyword allows multi-statement branches in switch expressions, enabling complex logic while still returning a value.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SwitchYieldExample</span> </span>{

    <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Operation</span> </span>{ ADD, SUBTRACT, MULTIPLY, DIVIDE }

    <span class="hljs-comment">// Simple yield: single statement in braces</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">double</span> <span class="hljs-title">calculate</span><span class="hljs-params">(Operation op, <span class="hljs-keyword">double</span> a, <span class="hljs-keyword">double</span> b)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">switch</span> (op) {
            <span class="hljs-keyword">case</span> ADD -&gt; {
                System.out.println(<span class="hljs-string">"Adding..."</span>);
                yield a + b;
            }
            <span class="hljs-keyword">case</span> SUBTRACT -&gt; {
                System.out.println(<span class="hljs-string">"Subtracting..."</span>);
                yield a - b;
            }
            <span class="hljs-keyword">case</span> MULTIPLY -&gt; a * b;
            <span class="hljs-keyword">case</span> DIVIDE -&gt; (b == <span class="hljs-number">0</span>) ? Double.NaN : a / b;
        };
    }

    <span class="hljs-comment">// Complex logic: validation with yield</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">getGrade</span><span class="hljs-params">(<span class="hljs-keyword">int</span> score)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">switch</span> (score / <span class="hljs-number">10</span>) {
            <span class="hljs-keyword">case</span> <span class="hljs-number">10</span>, <span class="hljs-number">9</span> -&gt; <span class="hljs-string">"A"</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-number">8</span> -&gt; <span class="hljs-string">"B"</span>;
            <span class="hljs-keyword">case</span> <span class="hljs-number">7</span> -&gt; <span class="hljs-string">"C"</span>;
            <span class="hljs-keyword">default</span> -&gt; {
                <span class="hljs-keyword">if</span> (score &lt; <span class="hljs-number">0</span> || score &gt; <span class="hljs-number">100</span>) {
                    <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Invalid: "</span> + score);
                }
                yield <span class="hljs-string">"F"</span>;
            }
        };
    }
}
</code></pre>
<p><strong>Test:</strong></p>
<pre><code class="lang-java"><span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldUseYieldForMultiStatementBranches</span><span class="hljs-params">()</span> </span>{
    assertEquals(<span class="hljs-number">15.0</span>, calculate(Operation.ADD, <span class="hljs-number">10</span>, <span class="hljs-number">5</span>), <span class="hljs-number">0.001</span>);
    assertEquals(<span class="hljs-string">"A"</span>, getGrade(<span class="hljs-number">95</span>));
    assertEquals(<span class="hljs-string">"F"</span>, getGrade(<span class="hljs-number">45</span>));
}
</code></pre>
<p><strong>Key Insight</strong>: The <code>yield</code> keyword enables multi-statement logic within switch branches. Unlike the arrow syntax (<code>-&gt;</code>), which executes a single expression, <code>yield</code> allows statements with side effects (like logging) before returning a value. This combines conciseness with flexibility.</p>
<hr />
<h3 id="heading-example-4-pattern-matching-in-switch-java-17">Example 4: Pattern Matching in Switch (Java 17+)</h3>
<p>Pattern matching in switch expressions (JEP 406) combines type matching, guards, and record destructuring into a single powerful feature.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PatternSwitchExample</span> </span>{

    <span class="hljs-comment">// Type patterns - match and extract different types</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">processObject</span><span class="hljs-params">(Object obj)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">switch</span> (obj) {
            <span class="hljs-keyword">case</span> String s -&gt; <span class="hljs-string">"String: "</span> + s;
            <span class="hljs-keyword">case</span> Integer i -&gt; <span class="hljs-string">"Integer: "</span> + i;
            <span class="hljs-keyword">case</span> Double d -&gt; <span class="hljs-string">"Double: "</span> + d;
            <span class="hljs-keyword">case</span> <span class="hljs-keyword">null</span> -&gt; <span class="hljs-string">"Null value"</span>;
            <span class="hljs-keyword">default</span> -&gt; <span class="hljs-string">"Unknown"</span>;
        };
    }

    <span class="hljs-comment">// Record patterns with guarded patterns</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{}

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">describePoint</span><span class="hljs-params">(Object obj)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">switch</span> (obj) {
            <span class="hljs-function"><span class="hljs-keyword">case</span> <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> when x &gt; 0 &amp;&amp; y &gt; 0 -&gt;
                "Quadrant 1: <span class="hljs-params">(<span class="hljs-string">" + x + "</span>, <span class="hljs-string">" + y + "</span>)</span>"</span>;
            <span class="hljs-function"><span class="hljs-keyword">case</span> <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> when x &lt; 0 &amp;&amp; y &gt; 0 -&gt;
                "Quadrant 2: <span class="hljs-params">(<span class="hljs-string">" + x + "</span>, <span class="hljs-string">" + y + "</span>)</span>"</span>;
            <span class="hljs-function"><span class="hljs-keyword">case</span> <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> when x </span>== <span class="hljs-number">0</span> &amp;&amp; y == <span class="hljs-number">0</span> -&gt;
                <span class="hljs-string">"Origin"</span>;
            <span class="hljs-function"><span class="hljs-keyword">case</span> <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> -&gt;
                "Other: <span class="hljs-params">(<span class="hljs-string">" + x + "</span>, <span class="hljs-string">" + y + "</span>)</span>"</span>;
            <span class="hljs-keyword">default</span> -&gt; <span class="hljs-string">"Not a point"</span>;
        };
    }
}
</code></pre>
<p><strong>Test:</strong></p>
<pre><code class="lang-java"><span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldUsePatternMatchingInSwitch</span><span class="hljs-params">()</span> </span>{
    assertEquals(<span class="hljs-string">"String: Hello"</span>,
        PatternSwitchExample.processObject(<span class="hljs-string">"Hello"</span>));
    assertEquals(<span class="hljs-string">"Null value"</span>,
        PatternSwitchExample.processObject(<span class="hljs-keyword">null</span>));
    assertEquals(<span class="hljs-string">"Quadrant 1: (3, 4)"</span>,
        PatternSwitchExample.describePoint(<span class="hljs-keyword">new</span> Point(<span class="hljs-number">3</span>, <span class="hljs-number">4</span>)));
}
</code></pre>
<p><strong>Key Insight</strong>: Pattern matching in switch combines type patterns (like <code>case String s</code>), guarded patterns (like <code>when x &gt; 0</code>), and record destructuring (like <code>case Point(int x, int y)</code>). This eliminates the need for instanceof checks and manual casting, resulting in cleaner, more type-safe code.</p>
<hr />
<h3 id="heading-example-5-pattern-matching-with-type-hierarchies">Example 5: Pattern Matching with Type Hierarchies</h3>
<p>When handling polymorphic types, pattern matching eliminates the redundancy of explicit casting. Instead of writing type names three times (instanceof, cast declaration, cast operation), do it once with pattern matching.</p>
<p><strong>Before Java 16 - Explicit casting required:</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">if</span> (shape <span class="hljs-keyword">instanceof</span> Circle) {
    Circle c = (Circle) shape;  <span class="hljs-comment">// ← Type name appears 3 times!</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Radius: "</span> + c.radius();
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (shape <span class="hljs-keyword">instanceof</span> Rectangle) {
    Rectangle r = (Rectangle) shape;  <span class="hljs-comment">// ← Redundant</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Width: "</span> + r.width();
}
</code></pre>
<p><strong>Java 16+ - Pattern matching eliminates redundancy:</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">if</span> (shape <span class="hljs-keyword">instanceof</span> Circle c) {  <span class="hljs-comment">// ← All three operations: check, cast, bind</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Radius: "</span> + c.radius();
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (shape <span class="hljs-keyword">instanceof</span> Rectangle r) {
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Width: "</span> + r.width();
}
</code></pre>
<p><strong>Full Example:</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TypeHierarchyExample</span> </span>{

    sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Shape</span> <span class="hljs-title">permits</span> <span class="hljs-title">Circle</span>, <span class="hljs-title">Rectangle</span>, <span class="hljs-title">Triangle</span> </span>{}
    <span class="hljs-function">record <span class="hljs-title">Circle</span><span class="hljs-params">(<span class="hljs-keyword">double</span> radius)</span> implements Shape </span>{}
    <span class="hljs-function">record <span class="hljs-title">Rectangle</span><span class="hljs-params">(<span class="hljs-keyword">double</span> width, <span class="hljs-keyword">double</span> height)</span> implements Shape </span>{}
    <span class="hljs-function">record <span class="hljs-title">Triangle</span><span class="hljs-params">(<span class="hljs-keyword">double</span> base, <span class="hljs-keyword">double</span> height)</span> implements Shape </span>{}

    <span class="hljs-comment">// Modern approach with pattern matching</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">describe</span><span class="hljs-params">(Shape shape)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">switch</span> (shape) {
            <span class="hljs-keyword">case</span> Circle c -&gt; <span class="hljs-string">"Circle: radius="</span> + c.radius();
            <span class="hljs-keyword">case</span> Rectangle r -&gt; <span class="hljs-string">"Rectangle: "</span> + r.width() + <span class="hljs-string">"x"</span> + r.height();
            <span class="hljs-keyword">case</span> Triangle t -&gt; <span class="hljs-string">"Triangle: base="</span> + t.base();
        };
    }
}
</code></pre>
<p><strong>Test:</strong></p>
<pre><code class="lang-java"><span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldEliminateTypeCastingWithPatternMatching</span><span class="hljs-params">()</span> </span>{
    Shape circle = <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">5.0</span>);
    Shape rect = <span class="hljs-keyword">new</span> Rectangle(<span class="hljs-number">10.0</span>, <span class="hljs-number">20.0</span>);

    assertEquals(<span class="hljs-string">"Circle: radius=5.0"</span>, describe(circle));
    assertTrue(describe(rect).contains(<span class="hljs-string">"Rectangle"</span>));
}
</code></pre>
<p><strong>Key Insight</strong>: Sealed types + pattern matching guarantee exhaustiveness. The compiler knows all Shape subtypes. When you use sealed types with switch pattern matching, no <code>default</code> case is needed—the compiler will error if you forget to handle a subtype. This prevents entire classes of runtime type errors.</p>
<hr />
<h3 id="heading-example-6-implementing-equals-with-pattern-matching">Example 6: Implementing equals() with Pattern Matching</h3>
<p>Pattern matching eliminates boilerplate from <code>equals()</code> methods. Instead of explicit null checks, type checks, and casting, pattern matching does it all in one line.</p>
<p><strong>Before Java 16 - 6 lines of ceremony:</strong></p>
<pre><code class="lang-java"><span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">equals</span><span class="hljs-params">(Object obj)</span> </span>{
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span> == obj) <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
    <span class="hljs-keyword">if</span> (obj == <span class="hljs-keyword">null</span>) <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    <span class="hljs-keyword">if</span> (getClass() != obj.getClass()) <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
    Point other = (Point) obj;              <span class="hljs-comment">// ← Manual cast after type check</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.x == other.x &amp;&amp; <span class="hljs-keyword">this</span>.y == other.y;
}
</code></pre>
<p><strong>Java 16+ - Pattern matching eliminates all boilerplate:</strong></p>
<pre><code class="lang-java"><span class="hljs-meta">@Override</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">equals</span><span class="hljs-params">(Object obj)</span> </span>{
    <span class="hljs-keyword">return</span> obj <span class="hljs-keyword">instanceof</span> Point p &amp;&amp;        <span class="hljs-comment">// ← Type check + cast + bind in ONE</span>
           <span class="hljs-keyword">this</span>.x == p.x &amp;&amp; <span class="hljs-keyword">this</span>.y == p.y;
}
</code></pre>
<p><strong>Full Example:</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EqualsPatternMatchingExample</span> </span>{

    <span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{
        <span class="hljs-meta">@Override</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">equals</span><span class="hljs-params">(Object obj)</span> </span>{
            <span class="hljs-keyword">return</span> obj <span class="hljs-keyword">instanceof</span> Point p &amp;&amp;
                   <span class="hljs-keyword">this</span>.x == p.x &amp;&amp; <span class="hljs-keyword">this</span>.y == p.y;
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Person</span><span class="hljs-params">(String name, <span class="hljs-keyword">int</span> age)</span> </span>{
        <span class="hljs-meta">@Override</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">equals</span><span class="hljs-params">(Object obj)</span> </span>{
            <span class="hljs-keyword">return</span> obj <span class="hljs-keyword">instanceof</span> Person p &amp;&amp;
                   Objects.equals(<span class="hljs-keyword">this</span>.name, p.name) &amp;&amp;
                   <span class="hljs-keyword">this</span>.age == p.age;
        }
    }
}
</code></pre>
<p><strong>Test:</strong></p>
<pre><code class="lang-java"><span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldUsePatternMatchingInEquals</span><span class="hljs-params">()</span> </span>{
    Point p1 = <span class="hljs-keyword">new</span> Point(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>);
    Point p2 = <span class="hljs-keyword">new</span> Point(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>);

    assertTrue(p1.equals(p2));
    assertFalse(p1.equals(<span class="hljs-keyword">null</span>));
}
</code></pre>
<p><strong>Key Insight</strong>: Pattern matching replaces 6 lines of <code>equals()</code> boilerplate with 1 line. The <code>instanceof</code> pattern automatically handles null (returns false), type checking, and casting—all atomically. This eliminates an entire class of bugs where type checks and casts could be misaligned.</p>
<hr />
<h3 id="heading-example-7-sealed-types-with-pattern-matching-result">Example 7: Sealed Types with Pattern Matching (Result)</h3>
<p>Pattern matching reaches its full potential with sealed types. A Result can only be Success or Failure—the compiler enforces exhaustiveness. Add a new type, and all pattern matching code fails to compile until updated.</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ResultPatternExample</span> </span>{

    <span class="hljs-keyword">public</span> sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Result</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-title">permits</span> <span class="hljs-title">Success</span>, <span class="hljs-title">Failure</span> </span>{}
    <span class="hljs-keyword">public</span> record Success&lt;T&gt;(T value) implements Result&lt;T&gt; {}
    <span class="hljs-keyword">public</span> record Failure&lt;T&gt;(String error) implements Result&lt;T&gt; {}

    <span class="hljs-comment">// Pattern matching with sealed types</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> &lt;T&gt; <span class="hljs-function">String <span class="hljs-title">describe</span><span class="hljs-params">(Result&lt;T&gt; result)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">switch</span> (result) {
            <span class="hljs-keyword">case</span> Success&lt;T&gt; s -&gt; <span class="hljs-string">"Success: "</span> + s.value();
            <span class="hljs-keyword">case</span> Failure&lt;T&gt; f -&gt; <span class="hljs-string">"Failure: "</span> + f.error();
            <span class="hljs-comment">// No default needed - compiler knows these are the only two cases!</span>
        };
    }

    <span class="hljs-comment">// Functional transformation preserving sealed type contracts</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> &lt;T, U&gt; <span class="hljs-function">Result&lt;U&gt; <span class="hljs-title">map</span><span class="hljs-params">(Result&lt;T&gt; result, Function&lt;T, U&gt; fn)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">switch</span> (result) {
            <span class="hljs-keyword">case</span> Success&lt;T&gt; s -&gt; <span class="hljs-keyword">new</span> Success&lt;&gt;(fn.apply(s.value()));
            <span class="hljs-keyword">case</span> Failure&lt;T&gt; f -&gt; <span class="hljs-keyword">new</span> Failure&lt;&gt;(f.error());
        };
    }
}
</code></pre>
<p><strong>Test:</strong></p>
<pre><code class="lang-java"><span class="hljs-meta">@Test</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldEnforceSealedTypeExhaustiveness</span><span class="hljs-params">()</span> </span>{
    Result&lt;String&gt; success = <span class="hljs-keyword">new</span> Success&lt;&gt;(<span class="hljs-string">"Value"</span>);
    Result&lt;String&gt; failure = <span class="hljs-keyword">new</span> Failure&lt;&gt;(<span class="hljs-string">"Error"</span>);

    assertEquals(<span class="hljs-string">"Success: Value"</span>, describe(success));
    assertEquals(<span class="hljs-string">"Failure: Error"</span>, describe(failure));
    assertEquals(<span class="hljs-string">"Success: 10"</span>,
        describe(map(<span class="hljs-keyword">new</span> Success&lt;&gt;(<span class="hljs-number">5</span>), x -&gt; x * <span class="hljs-number">2</span>)));
}
</code></pre>
<p><strong>Key Insight</strong>: Sealed types + pattern matching = compiler enforced exhaustiveness. The compiler knows Success and Failure are the ONLY two cases. If you add a new Result type, all pattern matching code fails to compile until updated. This catches bugs at compile-time instead of runtime, and no default case is needed.</p>
<hr />
<h2 id="heading-best-practices">Best Practices</h2>
<h3 id="heading-when-to-use-pattern-matching">When to Use Pattern Matching</h3>
<p>✅ <strong>Use pattern matching for:</strong></p>
<ul>
<li><p><strong>Polymorphic method dispatch</strong> - replacing instanceof chains</p>
</li>
<li><p><strong>equals() implementations</strong> - cleaner than traditional approach</p>
</li>
<li><p><strong>Validation with type checks</strong> - combining instanceof with business logic</p>
</li>
<li><p><strong>Guard conditions</strong> - when you need type + additional condition</p>
</li>
</ul>
<p>❌ <strong>Don't use pattern matching when:</strong></p>
<ul>
<li><p><strong>Polymorphism would be better</strong> - if you control the hierarchy, add methods instead</p>
</li>
<li><p><strong>Type checks indicate design issues</strong> - consider refactoring to avoid type checking</p>
</li>
<li><p><strong>Performance is absolutely critical</strong> - pattern matching has negligible overhead, but direct method calls are still faster</p>
</li>
</ul>
<h3 id="heading-when-to-use-switch-expressions">When to Use Switch Expressions</h3>
<p>✅ <strong>Use switch expressions for:</strong></p>
<ul>
<li><p><strong>Mapping enum values</strong> - exhaustiveness checking prevents bugs</p>
</li>
<li><p><strong>Returning values from multi-way branches</strong> - cleaner than if-else chains</p>
</li>
<li><p><strong>Complex conditional logic</strong> - when multiple conditions determine a result</p>
</li>
</ul>
<p>❌ <strong>Don't use switch expressions when:</strong></p>
<ul>
<li><p><strong>A single if-else would suffice</strong> - don't over-engineer</p>
</li>
<li><p><strong>You need fall-through</strong> - rare, but use colon syntax if needed</p>
</li>
<li><p><strong>Cases have complex, unrelated logic</strong> - consider separate methods</p>
</li>
</ul>
<h3 id="heading-style-guidelines">Style Guidelines</h3>
<p><strong>Prefer modern patterns:</strong></p>
<pre><code class="lang-java"><span class="hljs-comment">// ✅ Good: concise and clear</span>
<span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> String s &amp;&amp; s.length() &gt; <span class="hljs-number">0</span>) {
    <span class="hljs-keyword">return</span> s.toUpperCase();
}

<span class="hljs-comment">// ❌ Avoid: old style</span>
<span class="hljs-keyword">if</span> (obj <span class="hljs-keyword">instanceof</span> String) {
    String s = (String) obj;
    <span class="hljs-keyword">if</span> (s.length() &gt; <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">return</span> s.toUpperCase();
    }
}
</code></pre>
<p><strong>Use meaningful variable names:</strong></p>
<pre><code class="lang-java"><span class="hljs-comment">// ✅ Good: descriptive names</span>
<span class="hljs-keyword">if</span> (shape <span class="hljs-keyword">instanceof</span> Circle c) {
    <span class="hljs-keyword">return</span> Math.PI * c.radius() * c.radius();
}

<span class="hljs-comment">// ❌ Avoid: single letters unless context is obvious</span>
<span class="hljs-keyword">if</span> (shape <span class="hljs-keyword">instanceof</span> Circle x) {
    <span class="hljs-keyword">return</span> Math.PI * x.radius() * x.radius();
}
</code></pre>
<p><strong>Group related cases in switch:</strong></p>
<pre><code class="lang-java"><span class="hljs-comment">// ✅ Good: logically grouped</span>
<span class="hljs-keyword">return</span> <span class="hljs-keyword">switch</span> (day) {
    <span class="hljs-keyword">case</span> MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY -&gt; <span class="hljs-string">"Weekday"</span>;
    <span class="hljs-keyword">case</span> SATURDAY, SUNDAY -&gt; <span class="hljs-string">"Weekend"</span>;
};

<span class="hljs-comment">// ❌ Avoid: unnecessarily separate</span>
<span class="hljs-keyword">return</span> <span class="hljs-keyword">switch</span> (day) {
    <span class="hljs-keyword">case</span> MONDAY -&gt; <span class="hljs-string">"Weekday"</span>;
    <span class="hljs-keyword">case</span> TUESDAY -&gt; <span class="hljs-string">"Weekday"</span>;
    <span class="hljs-comment">// ... repetitive</span>
};
</code></pre>
<hr />
<h2 id="heading-summary-and-next-steps">Summary and Next Steps</h2>
<h3 id="heading-what-we-covered"><strong>What we covered:</strong></h3>
<ol>
<li><p><strong>Pattern matching for instanceof</strong> (Java 16) eliminates test-and-cast ceremony</p>
</li>
<li><p><strong>Switch expressions</strong> (Java 14) make switch a first-class expression with exhaustiveness</p>
</li>
<li><p><strong>Arrow syntax (</strong><code>-&gt;</code>) prevents fall-through bugs</p>
</li>
<li><p><strong>yield keyword</strong> enables multi-statement branches in switch expressions</p>
</li>
<li><p><strong>Pattern variables</strong> have compiler-enforced scope based on flow analysis</p>
</li>
<li><p><strong>Combining patterns with guards</strong> creates powerful conditional logic</p>
</li>
<li><p><strong>Sealed types + pattern matching</strong> = exhaustiveness guarantees</p>
</li>
</ol>
<h3 id="heading-migration-strategy">Migration Strategy</h3>
<p>For teams upgrading from Java 8:</p>
<p><strong>Phase 1: Switch Expressions</strong></p>
<ul>
<li><p>Replace switch statements returning values with switch expressions</p>
</li>
<li><p>Use arrow syntax for new code</p>
</li>
<li><p>Keep colon syntax for rare fall-through cases</p>
</li>
</ul>
<p><strong>Phase 2: Pattern Matching</strong></p>
<ul>
<li><p>Update <code>equals()</code> methods to use pattern matching</p>
</li>
<li><p>Replace instanceof-cast chains with pattern matching</p>
</li>
<li><p>Add guard conditions where appropriate</p>
</li>
</ul>
<p><strong>Phase 3: Combined Patterns</strong></p>
<ul>
<li><p>Refactor complex type hierarchies with sealed types + pattern matching</p>
</li>
<li><p>Leverage exhaustiveness checking for safer refactoring</p>
</li>
</ul>
<h3 id="heading-resources">Resources</h3>
<h4 id="heading-official-documentation">Official Documentation</h4>
<ul>
<li><p><strong>JEP 394 - Pattern Matching for instanceof</strong>: <a target="_blank" href="https://openjdk.org/jeps/394">openjdk.org/jeps/394</a> The official proposal introducing pattern matching for instanceof in Java 16. Includes design rationale, scope rules, and future directions.</p>
</li>
<li><p><strong>JEP 361 - Switch Expressions</strong>: <a target="_blank" href="https://openjdk.org/jeps/361">openjdk.org/jeps/361</a> Finalized switch expressions in Java 14. Details arrow syntax, yield keyword, and exhaustiveness requirements.</p>
</li>
<li><p><strong>JEP 325 - Switch Expressions (Preview)</strong>: <a target="_blank" href="https://openjdk.org/jeps/325">openjdk.org/jeps/325</a> First preview in Java 12, showing the evolution of the feature.</p>
</li>
<li><p><strong>JEP 354 - Switch Expressions (Second Preview)</strong>: <a target="_blank" href="https://openjdk.org/jeps/354">openjdk.org/jeps/354</a> Refinements in Java 13 based on community feedback.</p>
</li>
</ul>
<h4 id="heading-interactive-references">Interactive References</h4>
<ul>
<li><p><strong>Java Almanac - instanceof Patterns</strong>: <a target="_blank" href="https://javaalmanac.io/features/instanceof-patterns/">javaalmanac.io/features/instanceof-patterns/</a> Interactive examples demonstrating pattern matching for instanceof with runnable code samples.</p>
</li>
<li><p><strong>Java Almanac - Switch</strong>: <a target="_blank" href="https://javaalmanac.io/features/switch/">javaalmanac.io/features/switch/</a> Comprehensive guide to switch expressions with visual timelines and comparisons.</p>
</li>
</ul>
<h4 id="heading-code-repository">Code Repository</h4>
<ul>
<li><p><strong>GitHub Repository - blog-9mac-dev-code</strong>: <a target="_blank" href="https://github.com/dawid-swist/blog-9mac-dev-code">github.com/dawid-swist/blog-9mac-dev-code</a> All examples from this article with full JUnit tests:</p>
<pre><code class="lang-bash">  git <span class="hljs-built_in">clone</span> https://github.com/dawid-swist/blog-9mac-dev-code.git
  <span class="hljs-built_in">cd</span> blog-post-examples/java/2025-10-25-java17-features-every-senior-developer-should-know
  ../../gradlew <span class="hljs-built_in">test</span> --tests *part4*
</code></pre>
</li>
</ul>
<h4 id="heading-additional-reading">Additional Reading</h4>
<ul>
<li><p><strong>Brian Goetz - "</strong><a target="_blank" href="https://openjdk.org/projects/amber/design-notes/patterns/pattern-match-object-model"><strong>Pattern Matching in the Java Object Model</strong></a><strong>"</strong>: Technical deep dive from Java's language architect</p>
</li>
<li><p><strong>Inside Java Podcast -</strong> <a target="_blank" href="https://www.youtube.com/watch?v=pnXlCvYspYk"><strong>Pattern Matching Episodes</strong></a>: Discussions on design decisions and future directions</p>
</li>
<li><p><strong>Effective Java (3rd Edition)</strong> - Item 14: Consider implementing Comparable Shows how pattern matching can simplify comparison logic</p>
</li>
</ul>
<hr />
<p><em>Written for</em> <a target="_blank" href="https://blog.9mac.dev"><em>blog.9mac.dev</em></a><em>Part of the "Java 17 Features Every Senior Developer Should Know" series</em></p>
<p><strong>Previous</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-3-sealed-classes">Part 3 - Sealed Classes</a> <strong>Next</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-5-text-blocks">Part 5 - Text Blocks</a></p>
<hr />
]]></content:encoded></item><item><title><![CDATA[Java 17 Features Every Senior Developer Should Know - Part 3: Sealed Classes]]></title><description><![CDATA[Welcome back to our comprehensive series on Java 17 features! In Part 1, we explored the var keyword and type inference. In Part 2, we discovered how Records eliminate boilerplate in data classes. Today, we're diving into Sealed Classes (JEP 409)—a f...]]></description><link>https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-3-sealed-classes</link><guid isPermaLink="true">https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-3-sealed-classes</guid><category><![CDATA[Java]]></category><category><![CDATA[java 17]]></category><dc:creator><![CDATA[Dawid Świst]]></dc:creator><pubDate>Tue, 11 Nov 2025 17:00:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1762880382498/7e199da8-f77e-499f-8cb9-5b1488ab7dbd.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome back to our comprehensive series on Java 17 features! In Part 1, we explored the <code>var</code> keyword and type inference. In Part 2, we discovered how Records eliminate boilerplate in data classes. Today, we're diving into <strong>Sealed Classes</strong> (JEP 409)—a feature that gives you fine-grained control over class hierarchies.</p>
<p>If you've ever wanted to restrict which classes can extend your base class without making it <code>final</code>, or if you've struggled with maintaining domain model integrity across large codebases, this article is for you. Sealed classes bring algebraic data types to Java, enabling exhaustive pattern matching and compiler-verified completeness checks.</p>
<hr />
<h2 id="heading-the-problem-uncontrolled-inheritance">The Problem: Uncontrolled Inheritance</h2>
<p>Before Java 17, you had two options for controlling inheritance:</p>
<ol>
<li><p><strong>Make the class</strong> <code>final</code> - No one can extend it at all</p>
</li>
<li><p><strong>Leave it open</strong> - Anyone, anywhere can extend it</p>
</li>
</ol>
<p>This binary choice created real problems in large codebases. Consider a payment processing system:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Before Java 17 - open hierarchy</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Payment</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">void</span> <span class="hljs-title">process</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CreditCardPayment</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Payment</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">process</span><span class="hljs-params">()</span> </span>{ <span class="hljs-comment">/* ... */</span> }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DebitCardPayment</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Payment</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">process</span><span class="hljs-params">()</span> </span>{ <span class="hljs-comment">/* ... */</span> }
}

<span class="hljs-comment">// Problem: Anyone can add new payment types!</span>
<span class="hljs-comment">// Some developer in another module:</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CryptoPayment</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Payment</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">process</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-comment">// Oops - we don't handle this in our switch statement!</span>
    }
}
</code></pre>
<p>The issues with uncontrolled inheritance:</p>
<ul>
<li><p><strong>No exhaustiveness checking</strong>: When you handle <code>Payment</code> types in a switch, the compiler can't verify you've covered all cases</p>
</li>
<li><p><strong>Domain model fragmentation</strong>: Core abstractions can be extended in unpredictable ways</p>
</li>
<li><p><strong>Breaking changes</strong>: Adding a new payment type should be a deliberate, coordinated change—not an afterthought</p>
</li>
<li><p><strong>Maintenance burden</strong>: Developers must constantly check for new subtypes in distant parts of the codebase</p>
</li>
</ul>
<h3 id="heading-why-final-and-open-classes-arent-enough">Why <code>final</code> and Open Classes Aren't Enough</h3>
<p>Making <code>Payment</code> final means you can't have <em>any</em> subtypes—but you need <code>CreditCardPayment</code> and <code>DebitCardPayment</code>. Leaving it open means <em>anyone</em> can add subtypes, breaking your carefully designed switch statements and validation logic.</p>
<p><strong>What we need:</strong> A middle ground where <code>Payment</code> can be extended, but only by the classes we explicitly permit.</p>
<hr />
<h2 id="heading-what-are-sealed-classes">What Are Sealed Classes?</h2>
<p>A <strong>sealed class</strong> is a class that restricts which other classes may extend it. You explicitly list the permitted subclasses using the <code>permits</code> clause:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> sealed <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Payment</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">CreditCardPayment</span>, <span class="hljs-title">DebitCardPayment</span>, <span class="hljs-title">CashPayment</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">void</span> <span class="hljs-title">process</span><span class="hljs-params">()</span></span>;
}
</code></pre>
<p>Now, <strong>only</strong> <code>CreditCardPayment</code>, <code>DebitCardPayment</code>, and <code>CashPayment</code> can extend <code>Payment</code>. Any attempt to create an unlisted subclass results in a compile-time error:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Compile error: CryptoPayment is not listed in permits clause</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CryptoPayment</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Payment</span> </span>{ }
</code></pre>
<h3 id="heading-the-three-subclass-modifiers">The Three Subclass Modifiers</h3>
<p>Every direct subclass of a sealed class must declare its own inheritance policy using one of three modifiers:</p>
<ol>
<li><p><code>final</code> - No further subclassing allowed</p>
</li>
<li><p><code>sealed</code> - Further subclassing permitted, but only to explicitly named subtypes</p>
</li>
<li><p><code>non-sealed</code> - Open to unrestricted subclassing (breaks the seal)</p>
</li>
</ol>
<pre><code class="lang-java"><span class="hljs-comment">// Option 1: final - no more extensions</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CreditCardPayment</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Payment</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">process</span><span class="hljs-params">()</span> </span>{ <span class="hljs-comment">/* ... */</span> }
}

<span class="hljs-comment">// Option 2: sealed - controlled multi-level hierarchy</span>
<span class="hljs-keyword">public</span> sealed <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BankTransfer</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Payment</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">DomesticTransfer</span>, <span class="hljs-title">InternationalTransfer</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">process</span><span class="hljs-params">()</span> </span>{ <span class="hljs-comment">/* ... */</span> }
}

<span class="hljs-comment">// Option 3: non-sealed - open for extension</span>
<span class="hljs-keyword">public</span> non-sealed <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CashPayment</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Payment</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">process</span><span class="hljs-params">()</span> </span>{ <span class="hljs-comment">/* ... */</span> }
}

<span class="hljs-comment">// Now anyone can extend CashPayment</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TippedCashPayment</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">CashPayment</span> </span>{ }
</code></pre>
<h3 id="heading-key-characteristics">Key Characteristics</h3>
<p>Sealed classes have specific properties:</p>
<ul>
<li><p><strong>Restricted inheritance</strong>: Only explicitly permitted classes can extend/implement</p>
</li>
<li><p><strong>Exhaustiveness checking</strong>: The compiler knows all possible subtypes for switch statements</p>
</li>
<li><p><strong>Same package/module requirement</strong>: Sealed class and permitted subclasses must be in the same package (or module)</p>
</li>
<li><p><strong>Sealed interfaces</strong>: Interfaces can be sealed identically to classes</p>
</li>
<li><p><strong>Records and sealed types</strong>: Records are implicitly <code>final</code>, suitable for sealed type implementations</p>
</li>
</ul>
<hr />
<h2 id="heading-history-of-sealed-classes">History of Sealed Classes</h2>
<p>Sealed classes evolved through three Java versions based on community feedback and real-world testing.</p>
<h3 id="heading-java-15-preview-feature-jep-360">Java 15: Preview Feature (JEP 360)</h3>
<p>Sealed types debuted in September 2020 as a <strong>preview feature</strong>, requiring the <code>--enable-preview</code> compiler flag. The basic syntax was introduced:</p>
<pre><code class="lang-java">sealed <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Shape</span> <span class="hljs-title">permits</span> <span class="hljs-title">Circle</span>, <span class="hljs-title">Rectangle</span>, <span class="hljs-title">Triangle</span> </span>{ }

<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Circle</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Shape</span> </span>{ }
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Rectangle</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Shape</span> </span>{ }
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Triangle</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Shape</span> </span>{ }
</code></pre>
<p><strong>What you got:</strong></p>
<ul>
<li><p>Basic <code>sealed</code> and <code>permits</code> keywords</p>
</li>
<li><p>Requirement for subclasses to use <code>final</code>, <code>sealed</code>, or <code>non-sealed</code></p>
</li>
<li><p>Compile-time verification of permitted subclass list</p>
</li>
</ul>
<p><strong>Limitations:</strong></p>
<ul>
<li><p>Preview status meant production use required caution</p>
</li>
<li><p>Limited IDE support and tooling</p>
</li>
<li><p>Syntax still being refined based on feedback</p>
</li>
</ul>
<h3 id="heading-java-16-second-preview-jep-397">Java 16: Second Preview (JEP 397)</h3>
<p>March 2021 brought refinements and clarifications:</p>
<p><strong>Improvements:</strong></p>
<ul>
<li><p><strong>Omitting</strong> <code>permits</code> - If all subclasses are in the same source file, <code>permits</code> can be omitted</p>
</li>
<li><p><strong>Better error messages</strong> - Clearer compile errors for seal violations</p>
</li>
<li><p><strong>Sealed interfaces</strong> - Full support for sealed interfaces, not just classes</p>
</li>
<li><p><strong>Record integration</strong> - Records work seamlessly as sealed type implementations</p>
</li>
</ul>
<pre><code class="lang-java"><span class="hljs-comment">// Java 16 - permits clause optional if subclasses in same file</span>
sealed <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Result</span> </span>{ }
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Success</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Result</span> </span>{ }
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Failure</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Result</span> </span>{ }
</code></pre>
<h3 id="heading-java-17-standard-feature-jep-409">Java 17: Standard Feature (JEP 409)</h3>
<p>September 2021 marked the graduation of sealed classes to a <strong>standard, permanent feature</strong>. No more <code>--enable-preview</code> flag needed.</p>
<p><strong>Final enhancements:</strong></p>
<ul>
<li><p><strong>Reflection API</strong> - <code>Class.isSealed()</code> and <code>Class.getPermittedSubclasses()</code></p>
</li>
<li><p><strong>Pattern matching synergy</strong> - Full integration with pattern matching features</p>
</li>
<li><p><strong>Exhaustive switch</strong> - Compiler recognizes when switch covers all sealed subtypes</p>
</li>
<li><p><strong>Production-ready</strong> - Full IDE support, stable API, comprehensive documentation</p>
</li>
</ul>
<hr />
<h2 id="heading-basic-syntax-and-features">Basic Syntax and Features</h2>
<h3 id="heading-declaration-syntax">Declaration Syntax</h3>
<p>The basic sealed class declaration consists of:</p>
<ol>
<li><p>The <code>sealed</code> modifier (or <code>sealed interface</code> for interfaces)</p>
</li>
<li><p>The class/interface name</p>
</li>
<li><p>The <code>permits</code> clause listing allowed subclasses</p>
</li>
<li><p>Optional class body</p>
</li>
</ol>
<pre><code class="lang-java"><span class="hljs-comment">// Sealed class with permits</span>
<span class="hljs-keyword">public</span> sealed <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Shape</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">Circle</span>, <span class="hljs-title">Rectangle</span>, <span class="hljs-title">Triangle</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">double</span> <span class="hljs-title">area</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-comment">// Sealed interface</span>
<span class="hljs-keyword">public</span> sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">JSONValue</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">JSONObject</span>, <span class="hljs-title">JSONArray</span>, <span class="hljs-title">JSONString</span>, <span class="hljs-title">JSONNumber</span>, <span class="hljs-title">JSONBoolean</span>, <span class="hljs-title">JSONNull</span> </span>{
    <span class="hljs-function">String <span class="hljs-title">toJson</span><span class="hljs-params">()</span></span>;
}
</code></pre>
<h3 id="heading-omitting-the-permits-clause">Omitting the <code>permits</code> Clause</h3>
<p>If all permitted subclasses are declared in the same source file, you can omit <code>permits</code>:</p>
<pre><code class="lang-java"><span class="hljs-comment">// All in one file - no permits needed</span>
sealed <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Result</span> </span>{ }
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Success</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Result</span> </span>{ }
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Failure</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Result</span> </span>{ }
</code></pre>
<p>The compiler infers the permitted subclasses from the file contents.</p>
<h3 id="heading-subclass-requirements">Subclass Requirements</h3>
<p><strong>Every direct subclass must:</strong></p>
<ol>
<li><p>Be in the same package as the sealed parent (or same module if using JPMS)</p>
</li>
<li><p>Explicitly extend/implement the sealed parent</p>
</li>
<li><p>Choose a modifier: <code>final</code>, <code>sealed</code>, or <code>non-sealed</code></p>
</li>
</ol>
<pre><code class="lang-java"><span class="hljs-comment">// ✅ Valid - final subclass</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Circle</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Shape</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> <span class="hljs-title">area</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> Math.PI * radius * radius; }
}

<span class="hljs-comment">// ✅ Valid - sealed subclass with its own hierarchy</span>
<span class="hljs-keyword">public</span> sealed <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Polygon</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Shape</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">Triangle</span>, <span class="hljs-title">Rectangle</span>, <span class="hljs-title">Pentagon</span> </span>{
    <span class="hljs-comment">// ...</span>
}

<span class="hljs-comment">// ✅ Valid - non-sealed subclass (breaks the seal)</span>
<span class="hljs-keyword">public</span> non-sealed <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FreeformShape</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Shape</span> </span>{
    <span class="hljs-comment">// Anyone can extend FreeformShape now</span>
}

<span class="hljs-comment">// ❌ Invalid - missing final/sealed/non-sealed</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">InvalidShape</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Shape</span> </span>{ } <span class="hljs-comment">// Compile error</span>
</code></pre>
<h3 id="heading-design-philosophy-why-sealed-controls-only-direct-children">Design Philosophy: Why Sealed Controls Only Direct Children</h3>
<p>You might wonder: why doesn't sealing a class control the entire inheritance chain below it? Why can I seal a parent class but allow a non-sealed child, which then opens the hierarchy to unlimited extensions?</p>
<p>This design decision reflects a fundamental tension between <strong>control</strong> and <strong>flexibility</strong>. Understanding why sealed classes work this way requires looking at a real-world example: the Java Collections Framework.</p>
<p><strong>The Collections Framework Problem</strong></p>
<p>Imagine if Java sealed the <code>Collection</code> interface like this:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Hypothetical sealed Collection (don't do this!)</span>
<span class="hljs-keyword">public</span> sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Collection</span>&lt;<span class="hljs-title">E</span>&gt;
    <span class="hljs-title">permits</span> <span class="hljs-title">List</span>, <span class="hljs-title">Set</span>, <span class="hljs-title">Queue</span> </span>{
    <span class="hljs-comment">// ...</span>
}

<span class="hljs-keyword">public</span> sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">List</span>&lt;<span class="hljs-title">E</span>&gt; <span class="hljs-keyword">extends</span> <span class="hljs-title">Collection</span>&lt;<span class="hljs-title">E</span>&gt;
    <span class="hljs-title">permits</span> <span class="hljs-title">ArrayList</span>, <span class="hljs-title">LinkedList</span>, <span class="hljs-title">Vector</span> </span>{
    <span class="hljs-comment">// ...</span>
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ArrayList</span>&lt;<span class="hljs-title">E</span>&gt; <span class="hljs-keyword">implements</span> <span class="hljs-title">List</span>&lt;<span class="hljs-title">E</span>&gt; </span>{
    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>This looks reasonable: <code>Collection</code> controls its direct children (<code>List</code>, <code>Set</code>, <code>Queue</code>), and <code>List</code> controls its implementations (<code>ArrayList</code>, <code>LinkedList</code>). But now consider what this breaks:</p>
<pre><code class="lang-java"><span class="hljs-comment">// User code - This will NOT compile!</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyCustomArrayList</span>&lt;<span class="hljs-title">E</span>&gt; <span class="hljs-keyword">extends</span> <span class="hljs-title">ArrayList</span>&lt;<span class="hljs-title">E</span>&gt; </span>{
    <span class="hljs-comment">// Add custom caching behavior</span>
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> E <span class="hljs-title">get</span><span class="hljs-params">(<span class="hljs-keyword">int</span> index)</span> </span>{
        <span class="hljs-comment">// ... custom logic ...</span>
    }
}
</code></pre>
<p><strong>Compilation error:</strong> <code>MyCustomArrayList</code> is not in the permits clause of <code>List</code>. Your entire ecosystem of third-party libraries breaks overnight—any code that extends <code>ArrayList</code>, <code>LinkedList</code>, <code>HashMap</code>, etc., becomes invalid.</p>
<p><strong>Why This Matters</strong></p>
<p>The Java ecosystem thrives on extensibility. Libraries like Guava, Eclipse Collections, and Apache Commons provide custom collection implementations that extend standard classes. If sealed classes controlled the entire hierarchy, you'd need permission from the Collections framework maintainers every time you wanted a custom implementation.</p>
<p>The sealed classes design recognizes this: <strong>each level in the hierarchy independently decides what it permits</strong>. This is the key insight.</p>
<h4 id="heading-pattern-1-full-control-chain-sealed-sealed-final">Pattern 1: Full Control Chain (sealed → sealed → final)</h4>
<p>When you want maximal control over the entire hierarchy, use sealed at each level:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Top level: sealed, permits intermediate levels</span>
<span class="hljs-keyword">public</span> sealed <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Animal</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">Mammal</span>, <span class="hljs-title">Bird</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> String <span class="hljs-title">sound</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-comment">// Middle level: sealed, permits leaf implementations</span>
<span class="hljs-keyword">public</span> sealed <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Mammal</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Animal</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">Dog</span>, <span class="hljs-title">Cat</span> </span>{
    <span class="hljs-comment">// ...</span>
}

<span class="hljs-comment">// Leaf level: final, no extensions</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Dog</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Mammal</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sound</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> <span class="hljs-string">"woof"</span>; }
}

<span class="hljs-comment">// Attempting to extend final fails</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ServiceDog</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Dog</span> </span>{ } <span class="hljs-comment">// ❌ Compile error: Dog is final</span>
</code></pre>
<p><strong>What's happening here:</strong></p>
<ul>
<li><p><code>Animal</code> controls who can extend it (only <code>Mammal</code> and <code>Bird</code>)</p>
</li>
<li><p><code>Mammal</code> independently controls its children (only <code>Dog</code> and <code>Cat</code>)</p>
</li>
<li><p><code>Dog</code> is final, so the chain ends</p>
</li>
<li><p>Each level is a checkpoint that can't be bypassed</p>
</li>
</ul>
<p>This creates a <strong>closed hierarchy</strong> where the compiler knows every possible type at each level.</p>
<h4 id="heading-pattern-2-breaking-the-chain-sealed-non-sealed-open">Pattern 2: Breaking the Chain (sealed → non-sealed → open)</h4>
<p>When you want to control the top level but allow flexibility below, use non-sealed as an escape hatch:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Top level: sealed, permits both sealed and non-sealed children</span>
<span class="hljs-keyword">public</span> sealed <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DatabaseConnection</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">ManagedConnection</span>, <span class="hljs-title">UserConnection</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">void</span> <span class="hljs-title">execute</span><span class="hljs-params">(String query)</span></span>;
}

<span class="hljs-comment">// Branch 1: sealed - tight control</span>
<span class="hljs-keyword">public</span> sealed <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ManagedConnection</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">DatabaseConnection</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">PooledConnection</span>, <span class="hljs-title">CachedConnection</span> </span>{
    <span class="hljs-comment">// Framework-controlled connections</span>
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PooledConnection</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ManagedConnection</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">execute</span><span class="hljs-params">(String query)</span> </span>{ <span class="hljs-comment">/* ... */</span> }
}

<span class="hljs-comment">// Branch 2: non-sealed - BREAKS THE SEAL</span>
<span class="hljs-keyword">public</span> non-sealed <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserConnection</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">DatabaseConnection</span> </span>{
    <span class="hljs-comment">// Users can extend this freely!</span>
}

<span class="hljs-comment">// Now users can create unlimited custom connections</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CustomDatabaseConnection</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">UserConnection</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">execute</span><span class="hljs-params">(String query)</span> </span>{ <span class="hljs-comment">/* custom logic */</span> }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LoggingConnection</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">UserConnection</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">execute</span><span class="hljs-params">(String query)</span> </span>{ <span class="hljs-comment">/* log then delegate */</span> }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MetricsConnection</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">UserConnection</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">execute</span><span class="hljs-params">(String query)</span> </span>{ <span class="hljs-comment">/* record metrics */</span> }
}
</code></pre>
<p><strong>Why this pattern exists:</strong></p>
<p>Non-sealed is the middle ground between:</p>
<ul>
<li><p><code>sealed</code>: Forces all implementations to be in your control (too restrictive for libraries)</p>
</li>
<li><p><code>open</code>: No control at all (doesn't solve the problems sealed classes address)</p>
</li>
</ul>
<p>With non-sealed, you get the best of both worlds:</p>
<ul>
<li><p>The top-level <code>DatabaseConnection</code> remains sealed—you control what kinds of connections exist at the highest level</p>
</li>
<li><p><code>UserConnection</code> explicitly breaks the seal—users understand they're entering an open-extension zone</p>
</li>
<li><p>Users can create unlimited custom implementations without modifying your code or getting compiler errors</p>
</li>
</ul>
<p>This is exactly how the actual Collections Framework would be designed if it were sealed today.</p>
<h4 id="heading-pattern-3-final-leaf-nodes-sealed-final">Pattern 3: Final Leaf Nodes (sealed → final)</h4>
<p>When you want to prevent further extension, use final:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> sealed <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentMethod</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">CardPayment</span>, <span class="hljs-title">BankTransfer</span>, <span class="hljs-title">CryptoCurrency</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">void</span> <span class="hljs-title">process</span><span class="hljs-params">(<span class="hljs-keyword">double</span> amount)</span></span>;
}

<span class="hljs-comment">// Branch 1: final - impossible to extend</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CardPayment</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">PaymentMethod</span> </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">process</span><span class="hljs-params">(<span class="hljs-keyword">double</span> amount)</span> </span>{
        <span class="hljs-comment">// PCI compliance: no extensions allowed for security</span>
    }
}

<span class="hljs-comment">// Branch 2: sealed - further subdivision possible</span>
<span class="hljs-keyword">public</span> sealed <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BankTransfer</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">PaymentMethod</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">DomesticTransfer</span>, <span class="hljs-title">InternationalTransfer</span> </span>{
    <span class="hljs-comment">// ...</span>
}

<span class="hljs-comment">// Attempting to extend final fails</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EnhancedCardPayment</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">CardPayment</span> </span>{ }
<span class="hljs-comment">// ❌ Compile error: CardPayment is final</span>
</code></pre>
<p><strong>Why use final here:</strong></p>
<p>Some types are architectural endpoints. <code>CardPayment</code> handles PCI-compliant operations—you don't want subclasses that might inadvertently bypass security checks. By making it <code>final</code>, you make this intention explicit and prevent dangerous extensions.</p>
<h4 id="heading-why-not-just-seal-everything">Why Not Just Seal Everything?</h4>
<p>If you sealed every level all the way down, you'd recreate the Collections Framework problem:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Too restrictive!</span>
sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Collection</span>&lt;<span class="hljs-title">E</span>&gt; <span class="hljs-title">permits</span> <span class="hljs-title">List</span>, <span class="hljs-title">Set</span>, <span class="hljs-title">Queue</span> </span>{ }
sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">List</span>&lt;<span class="hljs-title">E</span>&gt; <span class="hljs-keyword">extends</span> <span class="hljs-title">Collection</span>&lt;<span class="hljs-title">E</span>&gt; <span class="hljs-title">permits</span> <span class="hljs-title">ArrayList</span>, <span class="hljs-title">LinkedList</span> </span>{ }
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ArrayList</span>&lt;<span class="hljs-title">E</span>&gt; <span class="hljs-keyword">implements</span> <span class="hljs-title">List</span>&lt;<span class="hljs-title">E</span>&gt; </span>{ }

<span class="hljs-comment">// Users can't extend ArrayList anymore - ecosystem breaks</span>
</code></pre>
<p>Instead, the actual design would be:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Practical design</span>
sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Collection</span>&lt;<span class="hljs-title">E</span>&gt; <span class="hljs-title">permits</span> <span class="hljs-title">List</span>, <span class="hljs-title">Set</span>, <span class="hljs-title">Queue</span> </span>{ }
non-sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">List</span>&lt;<span class="hljs-title">E</span>&gt; <span class="hljs-keyword">extends</span> <span class="hljs-title">Collection</span>&lt;<span class="hljs-title">E</span>&gt; </span>{ }
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ArrayList</span>&lt;<span class="hljs-title">E</span>&gt; <span class="hljs-keyword">implements</span> <span class="hljs-title">List</span>&lt;<span class="hljs-title">E</span>&gt; </span>{ }

<span class="hljs-comment">// Users can still extend ArrayList - ecosystem preserved</span>
</code></pre>
<p>By using <code>non-sealed</code> strategically, you:</p>
<ul>
<li><p><strong>Establish intent</strong> at the top level (what types of collections exist?)</p>
</li>
<li><p><strong>Allow flexibility</strong> where it matters (users can extend implementations)</p>
</li>
<li><p><strong>Prevent accidents</strong> (exhaustive switches over the four main collection types)</p>
</li>
<li><p><strong>Document constraints</strong> (some sealed branches forbid extensions, others allow them)</p>
</li>
</ul>
<h3 id="heading-sealed-interfaces">Sealed Interfaces</h3>
<p>Interfaces work identically to classes:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Transport</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">Car</span>, <span class="hljs-title">Bicycle</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">move</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-comment">// Records are implicitly final</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Car</span><span class="hljs-params">(String model, <span class="hljs-keyword">int</span> passengers)</span> implements Transport </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">move</span><span class="hljs-params">()</span> </span>{ System.out.println(<span class="hljs-string">"Driving "</span> + model); }
}

<span class="hljs-comment">// Enum values are implicitly final</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Bicycle</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Transport</span> </span>{
    MOUNTAIN, ROAD, HYBRID;

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">move</span><span class="hljs-params">()</span> </span>{ System.out.println(<span class="hljs-string">"Pedaling "</span> + <span class="hljs-keyword">this</span>); }
}
</code></pre>
<h3 id="heading-reflection-api">Reflection API</h3>
<p>Java 17 adds reflection methods for sealed types:</p>
<pre><code class="lang-java">Class&lt;?&gt; shapeClass = Shape.class;

<span class="hljs-comment">// Check if sealed</span>
<span class="hljs-keyword">boolean</span> isSealed = shapeClass.isSealed(); <span class="hljs-comment">// true</span>

<span class="hljs-comment">// Get permitted subclasses</span>
Class&lt;?&gt;[] permitted = shapeClass.getPermittedSubclasses();
<span class="hljs-comment">// Returns: [Circle.class, Rectangle.class, Triangle.class]</span>
</code></pre>
<hr />
<h2 id="heading-practical-examples">Practical Examples</h2>
<h3 id="heading-example-1-basic-sealed-classes-with-shape-hierarchy">Example 1: Basic Sealed Classes with Shape Hierarchy</h3>
<p>Sealed classes provide controlled inheritance hierarchies where the compiler knows all possible subtypes, enabling exhaustive pattern matching.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Sealed parent class</span>
<span class="hljs-keyword">public</span> sealed <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Shape</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">Circle</span>, <span class="hljs-title">Rectangle</span>, <span class="hljs-title">Triangle</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> <span class="hljs-keyword">double</span> <span class="hljs-title">area</span><span class="hljs-params">()</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> String <span class="hljs-title">describe</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-comment">// Final implementations</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Circle</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Shape</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">double</span> radius;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Circle</span><span class="hljs-params">(<span class="hljs-keyword">double</span> radius)</span> </span>{
        <span class="hljs-keyword">if</span> (radius &lt;= <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Radius must be positive"</span>);
        }
        <span class="hljs-keyword">this</span>.radius = radius;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> <span class="hljs-title">area</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> Math.PI * radius * radius;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">describe</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> String.format(<span class="hljs-string">"Circle[radius=%.2f, area=%.2f]"</span>, radius, area());
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> <span class="hljs-title">radius</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> radius; }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Rectangle</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Shape</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">double</span> width;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">double</span> height;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Rectangle</span><span class="hljs-params">(<span class="hljs-keyword">double</span> width, <span class="hljs-keyword">double</span> height)</span> </span>{
        <span class="hljs-keyword">if</span> (width &lt;= <span class="hljs-number">0</span> || height &lt;= <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Dimensions must be positive"</span>);
        }
        <span class="hljs-keyword">this</span>.width = width;
        <span class="hljs-keyword">this</span>.height = height;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> <span class="hljs-title">area</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> width * height;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">describe</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> String.format(<span class="hljs-string">"Rectangle[width=%.2f, height=%.2f, area=%.2f]"</span>,
            width, height, area());
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> <span class="hljs-title">width</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> width; }
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> <span class="hljs-title">height</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> height; }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Triangle</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Shape</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">double</span> base;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">double</span> height;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Triangle</span><span class="hljs-params">(<span class="hljs-keyword">double</span> base, <span class="hljs-keyword">double</span> height)</span> </span>{
        <span class="hljs-keyword">if</span> (base &lt;= <span class="hljs-number">0</span> || height &lt;= <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Dimensions must be positive"</span>);
        }
        <span class="hljs-keyword">this</span>.base = base;
        <span class="hljs-keyword">this</span>.height = height;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> <span class="hljs-title">area</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-number">0.5</span> * base * height;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">describe</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> String.format(<span class="hljs-string">"Triangle[base=%.2f, height=%.2f, area=%.2f]"</span>,
            base, height, area());
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> <span class="hljs-title">base</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> base; }
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> <span class="hljs-title">height</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> height; }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BasicSealedExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-keyword">var</span> shapes = List.of(
            <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">5.0</span>),
            <span class="hljs-keyword">new</span> Rectangle(<span class="hljs-number">4.0</span>, <span class="hljs-number">6.0</span>),
            <span class="hljs-keyword">new</span> Triangle(<span class="hljs-number">3.0</span>, <span class="hljs-number">8.0</span>)
        );

        System.out.println(<span class="hljs-string">"=== Shape Demonstrations ==="</span>);
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> shape : shapes) {
            System.out.println(shape.describe());
        }

        <span class="hljs-comment">// Type-specific operations</span>
        System.out.println(<span class="hljs-string">"\n=== Type-Specific Access ==="</span>);
        <span class="hljs-keyword">var</span> circle = <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">10.0</span>);
        System.out.println(<span class="hljs-string">"Circle radius: "</span> + circle.radius());

        <span class="hljs-keyword">var</span> rect = <span class="hljs-keyword">new</span> Rectangle(<span class="hljs-number">5.0</span>, <span class="hljs-number">3.0</span>);
        System.out.println(<span class="hljs-string">"Rectangle dimensions: "</span> + rect.width() + <span class="hljs-string">" x "</span> + rect.height());

        <span class="hljs-comment">// Reflection - discovering sealed hierarchy</span>
        System.out.println(<span class="hljs-string">"\n=== Reflection ==="</span>);
        System.out.println(<span class="hljs-string">"Shape is sealed: "</span> + Shape.class.isSealed());
        System.out.print(<span class="hljs-string">"Permitted subclasses: "</span>);
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> permitted : Shape.class.getPermittedSubclasses()) {
            System.out.print(permitted.getSimpleName() + <span class="hljs-string">" "</span>);
        }
        System.out.println();
    }
}
</code></pre>
<pre><code class="lang-java"><span class="hljs-comment">// Unit tests</span>

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should calculate circle area correctly")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCalculateCircleAreaCorrectly</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> circle = <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">5.0</span>);

    assertEquals(Math.PI * <span class="hljs-number">25</span>, circle.area(), <span class="hljs-number">0.001</span>);
    assertEquals(<span class="hljs-number">5.0</span>, circle.radius());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should calculate rectangle area correctly")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCalculateRectangleAreaCorrectly</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> rectangle = <span class="hljs-keyword">new</span> Rectangle(<span class="hljs-number">4.0</span>, <span class="hljs-number">6.0</span>);

    assertEquals(<span class="hljs-number">24.0</span>, rectangle.area(), <span class="hljs-number">0.001</span>);
    assertEquals(<span class="hljs-number">4.0</span>, rectangle.width());
    assertEquals(<span class="hljs-number">6.0</span>, rectangle.height());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should calculate triangle area correctly")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCalculateTriangleAreaCorrectly</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> triangle = <span class="hljs-keyword">new</span> Triangle(<span class="hljs-number">3.0</span>, <span class="hljs-number">8.0</span>);

    assertEquals(<span class="hljs-number">12.0</span>, triangle.area(), <span class="hljs-number">0.001</span>);
    assertEquals(<span class="hljs-number">3.0</span>, triangle.base());
    assertEquals(<span class="hljs-number">8.0</span>, triangle.height());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should verify Shape is sealed with correct permitted subclasses")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldVerifyShapeIsSealedWithCorrectPermittedSubclasses</span><span class="hljs-params">()</span> </span>{
    assertTrue(Shape.class.isSealed());

    <span class="hljs-keyword">var</span> permitted = Shape.class.getPermittedSubclasses();
    assertEquals(<span class="hljs-number">3</span>, permitted.length);

    <span class="hljs-keyword">var</span> permittedNames = Arrays.stream(permitted)
        .map(Class::getSimpleName)
        .collect(Collectors.toSet());

    assertTrue(permittedNames.contains(<span class="hljs-string">"Circle"</span>));
    assertTrue(permittedNames.contains(<span class="hljs-string">"Rectangle"</span>));
    assertTrue(permittedNames.contains(<span class="hljs-string">"Triangle"</span>));
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should throw exception for invalid dimensions")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldThrowExceptionForInvalidDimensions</span><span class="hljs-params">()</span> </span>{
    assertThrows(IllegalArgumentException.class, () -&gt; <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">0</span>));
    assertThrows(IllegalArgumentException.class, () -&gt; <span class="hljs-keyword">new</span> Circle(-<span class="hljs-number">5</span>));
    assertThrows(IllegalArgumentException.class, () -&gt; <span class="hljs-keyword">new</span> Rectangle(<span class="hljs-number">0</span>, <span class="hljs-number">5</span>));
    assertThrows(IllegalArgumentException.class, () -&gt; <span class="hljs-keyword">new</span> Triangle(<span class="hljs-number">5</span>, -<span class="hljs-number">3</span>));
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">=== Shape Demonstrations ===
Circle[radius=5.00, area=78.54]
Rectangle[width=4.00, height=6.00, area=24.00]
Triangle[base=3.00, height=8.00, area=12.00]

=== Type-Specific Access ===
Circle radius: 10.0
Rectangle dimensions: 5.0 x 3.0

=== Reflection ===
Shape is sealed: true
Permitted subclasses: Circle Rectangle Triangle
</code></pre>
<p><strong>Key Insight</strong>: Sealed classes give you controlled inheritance hierarchies. Unlike <code>final</code> (no extensions) or open classes (unlimited extensions), sealed classes let you explicitly list permitted subtypes. The compiler enforces this at compile-time, preventing unauthorized extensions.</p>
<hr />
<h3 id="heading-example-2-sealed-interfaces-with-records">Example 2: Sealed Interfaces with Records</h3>
<p>Sealed interfaces restrict implementations to explicit types with exhaustive checking.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Sealed interface</span>
<span class="hljs-keyword">public</span> sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Payment</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">CreditCard</span>, <span class="hljs-title">DebitCard</span>, <span class="hljs-title">Cash</span>, <span class="hljs-title">BankTransfer</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">double</span> <span class="hljs-title">amount</span><span class="hljs-params">()</span></span>;
    <span class="hljs-function">String <span class="hljs-title">description</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-comment">// Records as implementations (implicitly final)</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">CreditCard</span><span class="hljs-params">(
    String cardNumber,
    String cardHolder,
    <span class="hljs-keyword">double</span> amount,
    String merchantId
)</span> implements Payment </span>{

    <span class="hljs-keyword">public</span> CreditCard {
        <span class="hljs-keyword">if</span> (amount &lt;= <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Amount must be positive"</span>);
        }
        <span class="hljs-keyword">if</span> (cardNumber == <span class="hljs-keyword">null</span> || cardNumber.length() != <span class="hljs-number">16</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Invalid card number"</span>);
        }
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">description</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> String.format(<span class="hljs-string">"Credit Card payment: $%.2f (Card ending %s)"</span>,
            amount, cardNumber.substring(<span class="hljs-number">12</span>));
    }
}

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">DebitCard</span><span class="hljs-params">(
    String cardNumber,
    String cardHolder,
    <span class="hljs-keyword">double</span> amount,
    String pin
)</span> implements Payment </span>{

    <span class="hljs-keyword">public</span> DebitCard {
        <span class="hljs-keyword">if</span> (amount &lt;= <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Amount must be positive"</span>);
        }
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">description</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> String.format(<span class="hljs-string">"Debit Card payment: $%.2f (Card ending %s)"</span>,
            amount, cardNumber.substring(<span class="hljs-number">12</span>));
    }
}

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Cash</span><span class="hljs-params">(
    <span class="hljs-keyword">double</span> amount,
    String currency
)</span> implements Payment </span>{

    <span class="hljs-keyword">public</span> Cash {
        <span class="hljs-keyword">if</span> (amount &lt;= <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Amount must be positive"</span>);
        }
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">description</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> String.format(<span class="hljs-string">"Cash payment: %.2f %s"</span>, amount, currency);
    }
}

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">BankTransfer</span><span class="hljs-params">(
    String fromAccount,
    String toAccount,
    <span class="hljs-keyword">double</span> amount,
    String reference
)</span> implements Payment </span>{

    <span class="hljs-keyword">public</span> BankTransfer {
        <span class="hljs-keyword">if</span> (amount &lt;= <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Amount must be positive"</span>);
        }
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">description</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> String.format(<span class="hljs-string">"Bank Transfer: $%.2f (Ref: %s)"</span>, amount, reference);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SealedInterfaceExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-keyword">var</span> payments = List.of(
            <span class="hljs-keyword">new</span> CreditCard(<span class="hljs-string">"1234567890123456"</span>, <span class="hljs-string">"John Doe"</span>, <span class="hljs-number">150.00</span>, <span class="hljs-string">"MERCHANT_001"</span>),
            <span class="hljs-keyword">new</span> DebitCard(<span class="hljs-string">"9876543210987654"</span>, <span class="hljs-string">"Jane Smith"</span>, <span class="hljs-number">75.50</span>, <span class="hljs-string">"1234"</span>),
            <span class="hljs-keyword">new</span> Cash(<span class="hljs-number">50.00</span>, <span class="hljs-string">"USD"</span>),
            <span class="hljs-keyword">new</span> BankTransfer(<span class="hljs-string">"ACC001"</span>, <span class="hljs-string">"ACC002"</span>, <span class="hljs-number">1000.00</span>, <span class="hljs-string">"INV-2024-001"</span>)
        );

        System.out.println(<span class="hljs-string">"=== Payment Processing ==="</span>);
        <span class="hljs-keyword">double</span> total = <span class="hljs-number">0.0</span>;
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> payment : payments) {
            System.out.println(payment.description());
            total += payment.amount();
        }

        System.out.printf(<span class="hljs-string">"\nTotal processed: $%.2f\n"</span>, total);

        <span class="hljs-comment">// Type-specific access</span>
        System.out.println(<span class="hljs-string">"\n=== Type-Specific Operations ==="</span>);
        <span class="hljs-keyword">var</span> creditCard = <span class="hljs-keyword">new</span> CreditCard(<span class="hljs-string">"1111222233334444"</span>, <span class="hljs-string">"Alice Brown"</span>, <span class="hljs-number">299.99</span>, <span class="hljs-string">"SHOP_123"</span>);
        System.out.println(<span class="hljs-string">"Card holder: "</span> + creditCard.cardHolder());
        System.out.println(<span class="hljs-string">"Merchant ID: "</span> + creditCard.merchantId());

        <span class="hljs-comment">// Reflection</span>
        System.out.println(<span class="hljs-string">"\n=== Interface Reflection ==="</span>);
        System.out.println(<span class="hljs-string">"Payment is sealed: "</span> + Payment.class.isSealed());
        System.out.print(<span class="hljs-string">"Permitted implementations: "</span>);
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> permitted : Payment.class.getPermittedSubclasses()) {
            System.out.print(permitted.getSimpleName() + <span class="hljs-string">" "</span>);
        }
        System.out.println();
    }
}
</code></pre>
<pre><code class="lang-java"><span class="hljs-comment">// Unit tests</span>

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create CreditCard payment with valid data")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateCreditCardPaymentWithValidData</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> payment = <span class="hljs-keyword">new</span> CreditCard(<span class="hljs-string">"1234567890123456"</span>, <span class="hljs-string">"John Doe"</span>, <span class="hljs-number">150.00</span>, <span class="hljs-string">"MERCHANT_001"</span>);

    assertEquals(<span class="hljs-number">150.00</span>, payment.amount());
    assertEquals(<span class="hljs-string">"John Doe"</span>, payment.cardHolder());
    assertEquals(<span class="hljs-string">"MERCHANT_001"</span>, payment.merchantId());
    assertTrue(payment.description().contains(<span class="hljs-string">"$150.00"</span>));
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create DebitCard payment with valid data")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateDebitCardPaymentWithValidData</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> payment = <span class="hljs-keyword">new</span> DebitCard(<span class="hljs-string">"9876543210987654"</span>, <span class="hljs-string">"Jane Smith"</span>, <span class="hljs-number">75.50</span>, <span class="hljs-string">"1234"</span>);

    assertEquals(<span class="hljs-number">75.50</span>, payment.amount());
    assertEquals(<span class="hljs-string">"Jane Smith"</span>, payment.cardHolder());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create Cash payment with currency")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateCashPaymentWithCurrency</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> payment = <span class="hljs-keyword">new</span> Cash(<span class="hljs-number">50.00</span>, <span class="hljs-string">"USD"</span>);

    assertEquals(<span class="hljs-number">50.00</span>, payment.amount());
    assertEquals(<span class="hljs-string">"USD"</span>, payment.currency());
    assertTrue(payment.description().contains(<span class="hljs-string">"USD"</span>));
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create BankTransfer with reference number")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateBankTransferWithReferenceNumber</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> payment = <span class="hljs-keyword">new</span> BankTransfer(<span class="hljs-string">"ACC001"</span>, <span class="hljs-string">"ACC002"</span>, <span class="hljs-number">1000.00</span>, <span class="hljs-string">"INV-2024-001"</span>);

    assertEquals(<span class="hljs-number">1000.00</span>, payment.amount());
    assertEquals(<span class="hljs-string">"INV-2024-001"</span>, payment.reference());
    assertTrue(payment.description().contains(<span class="hljs-string">"INV-2024-001"</span>));
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should verify Payment interface is sealed")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldVerifyPaymentInterfaceIsSealed</span><span class="hljs-params">()</span> </span>{
    assertTrue(Payment.class.isSealed());

    <span class="hljs-keyword">var</span> permitted = Payment.class.getPermittedSubclasses();
    assertEquals(<span class="hljs-number">4</span>, permitted.length);
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should throw exception for invalid payment amounts")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldThrowExceptionForInvalidPaymentAmounts</span><span class="hljs-params">()</span> </span>{
    assertThrows(IllegalArgumentException.class,
        () -&gt; <span class="hljs-keyword">new</span> CreditCard(<span class="hljs-string">"1234567890123456"</span>, <span class="hljs-string">"John"</span>, <span class="hljs-number">0</span>, <span class="hljs-string">"M001"</span>));
    assertThrows(IllegalArgumentException.class,
        () -&gt; <span class="hljs-keyword">new</span> Cash(-<span class="hljs-number">10</span>, <span class="hljs-string">"USD"</span>));
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should throw exception for invalid card number")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldThrowExceptionForInvalidCardNumber</span><span class="hljs-params">()</span> </span>{
    assertThrows(IllegalArgumentException.class,
        () -&gt; <span class="hljs-keyword">new</span> CreditCard(<span class="hljs-string">"123"</span>, <span class="hljs-string">"John"</span>, <span class="hljs-number">100</span>, <span class="hljs-string">"M001"</span>));
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">=== Payment Processing ===
Credit Card payment: $150.00 (Card ending 3456)
Debit Card payment: $75.50 (Card ending 7654)
Cash payment: 50.00 USD
Bank Transfer: $1000.00 (Ref: INV-2024-001)

Total processed: $1275.50

=== Type-Specific Operations ===
Card holder: Alice Brown
Merchant ID: SHOP_123

=== Interface Reflection ===
Payment is sealed: true
Permitted implementations: CreditCard DebitCard Cash BankTransfer
</code></pre>
<p><strong>Key Insight</strong>: Records are implicitly <code>final</code>, making them suitable for sealed interface implementations. The compiler knows all possible implementations.</p>
<hr />
<h3 id="heading-example-3-multi-level-sealed-hierarchies">Example 3: Multi-level Sealed Hierarchies</h3>
<p>Sealed classes can form multi-level hierarchies where intermediate levels are also sealed, creating tree-like type structures.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Top-level sealed class</span>
<span class="hljs-keyword">public</span> sealed <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Vehicle</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">MotorVehicle</span>, <span class="hljs-title">Bicycle</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String brand;

    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-title">Vehicle</span><span class="hljs-params">(String brand)</span> </span>{
        <span class="hljs-keyword">this</span>.brand = brand;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">brand</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> brand; }
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> String <span class="hljs-title">describe</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-comment">// Second-level sealed class</span>
<span class="hljs-keyword">public</span> sealed <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MotorVehicle</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Vehicle</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">Car</span>, <span class="hljs-title">Motorcycle</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> engineCC;

    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-title">MotorVehicle</span><span class="hljs-params">(String brand, <span class="hljs-keyword">int</span> engineCC)</span> </span>{
        <span class="hljs-keyword">super</span>(brand);
        <span class="hljs-keyword">this</span>.engineCC = engineCC;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">engineCC</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> engineCC; }
}

<span class="hljs-comment">// Final implementations at third level</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Car</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">MotorVehicle</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> doors;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">boolean</span> isElectric;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Car</span><span class="hljs-params">(String brand, <span class="hljs-keyword">int</span> engineCC, <span class="hljs-keyword">int</span> doors, <span class="hljs-keyword">boolean</span> isElectric)</span> </span>{
        <span class="hljs-keyword">super</span>(brand, engineCC);
        <span class="hljs-keyword">this</span>.doors = doors;
        <span class="hljs-keyword">this</span>.isElectric = isElectric;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">doors</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> doors; }
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isElectric</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> isElectric; }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">describe</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> String.format(<span class="hljs-string">"Car[brand=%s, engineCC=%d, doors=%d, electric=%b]"</span>,
            brand(), engineCC(), doors, isElectric);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Motorcycle</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">MotorVehicle</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">boolean</span> hasSidecar;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Motorcycle</span><span class="hljs-params">(String brand, <span class="hljs-keyword">int</span> engineCC, <span class="hljs-keyword">boolean</span> hasSidecar)</span> </span>{
        <span class="hljs-keyword">super</span>(brand, engineCC);
        <span class="hljs-keyword">this</span>.hasSidecar = hasSidecar;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">hasSidecar</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> hasSidecar; }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">describe</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> String.format(<span class="hljs-string">"Motorcycle[brand=%s, engineCC=%d, hasSidecar=%b]"</span>,
            brand(), engineCC(), hasSidecar);
    }
}

<span class="hljs-comment">// Final implementation at second level</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Bicycle</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Vehicle</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> gears;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String type; <span class="hljs-comment">// "mountain", "road", "hybrid"</span>

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Bicycle</span><span class="hljs-params">(String brand, <span class="hljs-keyword">int</span> gears, String type)</span> </span>{
        <span class="hljs-keyword">super</span>(brand);
        <span class="hljs-keyword">this</span>.gears = gears;
        <span class="hljs-keyword">this</span>.type = type;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">gears</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> gears; }
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">type</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> type; }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">describe</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> String.format(<span class="hljs-string">"Bicycle[brand=%s, gears=%d, type=%s]"</span>,
            brand(), gears, type);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MultiLevelSealedExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-keyword">var</span> vehicles = List.of(
            <span class="hljs-keyword">new</span> Car(<span class="hljs-string">"Tesla"</span>, <span class="hljs-number">0</span>, <span class="hljs-number">4</span>, <span class="hljs-keyword">true</span>),
            <span class="hljs-keyword">new</span> Car(<span class="hljs-string">"BMW"</span>, <span class="hljs-number">3000</span>, <span class="hljs-number">2</span>, <span class="hljs-keyword">false</span>),
            <span class="hljs-keyword">new</span> Motorcycle(<span class="hljs-string">"Harley-Davidson"</span>, <span class="hljs-number">1200</span>, <span class="hljs-keyword">false</span>),
            <span class="hljs-keyword">new</span> Motorcycle(<span class="hljs-string">"Ural"</span>, <span class="hljs-number">750</span>, <span class="hljs-keyword">true</span>),
            <span class="hljs-keyword">new</span> Bicycle(<span class="hljs-string">"Trek"</span>, <span class="hljs-number">21</span>, <span class="hljs-string">"mountain"</span>),
            <span class="hljs-keyword">new</span> Bicycle(<span class="hljs-string">"Specialized"</span>, <span class="hljs-number">18</span>, <span class="hljs-string">"road"</span>)
        );

        System.out.println(<span class="hljs-string">"=== Vehicle Inventory ==="</span>);
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> vehicle : vehicles) {
            System.out.println(vehicle.describe());
        }

        <span class="hljs-comment">// Categorize by type</span>
        System.out.println(<span class="hljs-string">"\n=== Categorization ==="</span>);
        <span class="hljs-keyword">long</span> motorVehicles = vehicles.stream()
            .filter(v -&gt; v <span class="hljs-keyword">instanceof</span> MotorVehicle)
            .count();
        <span class="hljs-keyword">long</span> bicycles = vehicles.stream()
            .filter(v -&gt; v <span class="hljs-keyword">instanceof</span> Bicycle)
            .count();

        System.out.println(<span class="hljs-string">"Motor Vehicles: "</span> + motorVehicles);
        System.out.println(<span class="hljs-string">"Bicycles: "</span> + bicycles);

        <span class="hljs-comment">// Type-specific operations</span>
        System.out.println(<span class="hljs-string">"\n=== Motor Vehicle Details ==="</span>);
        <span class="hljs-keyword">var</span> car = <span class="hljs-keyword">new</span> Car(<span class="hljs-string">"Audi"</span>, <span class="hljs-number">2000</span>, <span class="hljs-number">4</span>, <span class="hljs-keyword">false</span>);
        System.out.println(<span class="hljs-string">"Car doors: "</span> + car.doors());
        System.out.println(<span class="hljs-string">"Engine CC: "</span> + car.engineCC());

        <span class="hljs-comment">// Hierarchy reflection</span>
        System.out.println(<span class="hljs-string">"\n=== Sealed Hierarchy ==="</span>);
        System.out.println(<span class="hljs-string">"Vehicle is sealed: "</span> + Vehicle.class.isSealed());
        System.out.println(<span class="hljs-string">"MotorVehicle is sealed: "</span> + MotorVehicle.class.isSealed());
        System.out.println(<span class="hljs-string">"Car is final: "</span> + java.lang.reflect.Modifier.isFinal(Car.class.getModifiers()));
    }
}
</code></pre>
<pre><code class="lang-java"><span class="hljs-comment">// Unit tests</span>

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create Car with correct properties")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateCarWithCorrectProperties</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> car = <span class="hljs-keyword">new</span> Car(<span class="hljs-string">"Tesla"</span>, <span class="hljs-number">0</span>, <span class="hljs-number">4</span>, <span class="hljs-keyword">true</span>);

    assertEquals(<span class="hljs-string">"Tesla"</span>, car.brand());
    assertEquals(<span class="hljs-number">0</span>, car.engineCC());
    assertEquals(<span class="hljs-number">4</span>, car.doors());
    assertTrue(car.isElectric());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create Motorcycle with correct properties")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateMotorcycleWithCorrectProperties</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> motorcycle = <span class="hljs-keyword">new</span> Motorcycle(<span class="hljs-string">"Harley-Davidson"</span>, <span class="hljs-number">1200</span>, <span class="hljs-keyword">false</span>);

    assertEquals(<span class="hljs-string">"Harley-Davidson"</span>, motorcycle.brand());
    assertEquals(<span class="hljs-number">1200</span>, motorcycle.engineCC());
    assertFalse(motorcycle.hasSidecar());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create Bicycle with correct properties")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateBicycleWithCorrectProperties</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> bicycle = <span class="hljs-keyword">new</span> Bicycle(<span class="hljs-string">"Trek"</span>, <span class="hljs-number">21</span>, <span class="hljs-string">"mountain"</span>);

    assertEquals(<span class="hljs-string">"Trek"</span>, bicycle.brand());
    assertEquals(<span class="hljs-number">21</span>, bicycle.gears());
    assertEquals(<span class="hljs-string">"mountain"</span>, bicycle.type());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should verify multi-level sealed hierarchy")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldVerifyMultiLevelSealedHierarchy</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-comment">// Top level is sealed</span>
    assertTrue(Vehicle.class.isSealed());
    <span class="hljs-keyword">var</span> vehiclePermitted = Vehicle.class.getPermittedSubclasses();
    assertEquals(<span class="hljs-number">2</span>, vehiclePermitted.length);

    <span class="hljs-comment">// Second level is sealed</span>
    assertTrue(MotorVehicle.class.isSealed());
    <span class="hljs-keyword">var</span> motorPermitted = MotorVehicle.class.getPermittedSubclasses();
    assertEquals(<span class="hljs-number">2</span>, motorPermitted.length);

    <span class="hljs-comment">// Leaf classes are final</span>
    assertTrue(java.lang.reflect.Modifier.isFinal(Car.class.getModifiers()));
    assertTrue(java.lang.reflect.Modifier.isFinal(Motorcycle.class.getModifiers()));
    assertTrue(java.lang.reflect.Modifier.isFinal(Bicycle.class.getModifiers()));
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should correctly identify MotorVehicle instances")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCorrectlyIdentifyMotorVehicleInstances</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> car = <span class="hljs-keyword">new</span> Car(<span class="hljs-string">"BMW"</span>, <span class="hljs-number">3000</span>, <span class="hljs-number">4</span>, <span class="hljs-keyword">false</span>);
    <span class="hljs-keyword">var</span> motorcycle = <span class="hljs-keyword">new</span> Motorcycle(<span class="hljs-string">"Yamaha"</span>, <span class="hljs-number">600</span>, <span class="hljs-keyword">false</span>);
    <span class="hljs-keyword">var</span> bicycle = <span class="hljs-keyword">new</span> Bicycle(<span class="hljs-string">"Giant"</span>, <span class="hljs-number">18</span>, <span class="hljs-string">"road"</span>);

    assertTrue(car <span class="hljs-keyword">instanceof</span> MotorVehicle);
    assertTrue(motorcycle <span class="hljs-keyword">instanceof</span> MotorVehicle);
    assertFalse(bicycle <span class="hljs-keyword">instanceof</span> MotorVehicle);
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">=== Vehicle Inventory ===
Car[brand=Tesla, engineCC=0, doors=4, electric=true]
Car[brand=BMW, engineCC=3000, doors=2, electric=false]
Motorcycle[brand=Harley-Davidson, engineCC=1200, hasSidecar=false]
Motorcycle[brand=Ural, engineCC=750, hasSidecar=true]
Bicycle[brand=Trek, gears=21, type=mountain]
Bicycle[brand=Specialized, gears=18, type=road]

=== Categorization ===
Motor Vehicles: 4
Bicycles: 2

=== Motor Vehicle Details ===
Car doors: 4
Engine CC: 2000

=== Sealed Hierarchy ===
Vehicle is sealed: true
MotorVehicle is sealed: true
Car is final: true
</code></pre>
<p><strong>Key Insight</strong>: Multi-level sealed hierarchies let you model complex domain structures with controlled inheritance at each level. The top-level <code>Vehicle</code> permits two branches: <code>MotorVehicle</code> (which is itself sealed) and <code>Bicycle</code> (which is final). This creates a tree structure where each node decides its children's extension policy.</p>
<hr />
<h3 id="heading-example-4-exhaustive-switch-with-sealed-types">Example 4: Exhaustive Switch with Sealed Types</h3>
<p>Sealed types enable exhaustive switch expressions. The compiler verifies all possible subtypes are handled without a <code>default</code> case.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Sealed JSON value hierarchy</span>
<span class="hljs-keyword">public</span> sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">JSONValue</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">JSONObject</span>, <span class="hljs-title">JSONArray</span>, <span class="hljs-title">JSONString</span>, <span class="hljs-title">JSONNumber</span>, <span class="hljs-title">JSONBoolean</span>, <span class="hljs-title">JSONNull</span> </span>{
    <span class="hljs-function">String <span class="hljs-title">toJson</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">JSONObject</span><span class="hljs-params">(java.util.Map&lt;String, JSONValue&gt; values)</span> implements JSONValue </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toJson</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> values.entrySet().stream()
            .map(e -&gt; <span class="hljs-string">"\""</span> + e.getKey() + <span class="hljs-string">"\":"</span> + e.getValue().toJson())
            .collect(java.util.stream.Collectors.joining(<span class="hljs-string">","</span>, <span class="hljs-string">"{"</span>, <span class="hljs-string">"}"</span>));
    }
}

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">JSONArray</span><span class="hljs-params">(java.util.List&lt;JSONValue&gt; values)</span> implements JSONValue </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toJson</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> values.stream()
            .map(JSONValue::toJson)
            .collect(java.util.stream.Collectors.joining(<span class="hljs-string">","</span>, <span class="hljs-string">"["</span>, <span class="hljs-string">"]"</span>));
    }
}

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">JSONString</span><span class="hljs-params">(String value)</span> implements JSONValue </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toJson</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"\""</span> + value.replace(<span class="hljs-string">"\""</span>, <span class="hljs-string">"\\\""</span>) + <span class="hljs-string">"\""</span>;
    }
}

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">JSONNumber</span><span class="hljs-params">(<span class="hljs-keyword">double</span> value)</span> implements JSONValue </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toJson</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> String.valueOf(value);
    }
}

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">JSONBoolean</span><span class="hljs-params">(<span class="hljs-keyword">boolean</span> value)</span> implements JSONValue </span>{
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toJson</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> String.valueOf(value);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">JSONNull</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">JSONValue</span> </span>{
    INSTANCE;

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toJson</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"null"</span>;
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ExhaustiveSwitchExample</span> </span>{
    <span class="hljs-comment">// Exhaustive switch - no default needed!</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> String <span class="hljs-title">describeType</span><span class="hljs-params">(JSONValue value)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">switch</span> (value) {
            <span class="hljs-keyword">case</span> JSONObject obj -&gt; <span class="hljs-string">"object with "</span> + obj.values().size() + <span class="hljs-string">" properties"</span>;
            <span class="hljs-keyword">case</span> JSONArray arr -&gt; <span class="hljs-string">"array with "</span> + arr.values().size() + <span class="hljs-string">" elements"</span>;
            <span class="hljs-keyword">case</span> JSONString str -&gt; <span class="hljs-string">"string: \""</span> + str.value() + <span class="hljs-string">"\""</span>;
            <span class="hljs-keyword">case</span> JSONNumber num -&gt; <span class="hljs-string">"number: "</span> + num.value();
            <span class="hljs-keyword">case</span> JSONBoolean bool -&gt; <span class="hljs-string">"boolean: "</span> + bool.value();
            <span class="hljs-keyword">case</span> JSONNull ignored -&gt; <span class="hljs-string">"null value"</span>;
            <span class="hljs-comment">// No default case needed - compiler knows all cases are covered!</span>
        };
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">estimateSize</span><span class="hljs-params">(JSONValue value)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">switch</span> (value) {
            <span class="hljs-keyword">case</span> JSONObject obj -&gt; obj.values().values().stream()
                .mapToInt(ExhaustiveSwitchExample::estimateSize)
                .sum() + <span class="hljs-number">2</span>; <span class="hljs-comment">// {} brackets</span>
            <span class="hljs-keyword">case</span> JSONArray arr -&gt; arr.values().stream()
                .mapToInt(ExhaustiveSwitchExample::estimateSize)
                .sum() + <span class="hljs-number">2</span>; <span class="hljs-comment">// [] brackets</span>
            <span class="hljs-keyword">case</span> JSONString str -&gt; str.value().length() + <span class="hljs-number">2</span>; <span class="hljs-comment">// quotes</span>
            <span class="hljs-keyword">case</span> JSONNumber num -&gt; String.valueOf(num.value()).length();
            <span class="hljs-keyword">case</span> JSONBoolean bool -&gt; String.valueOf(bool.value()).length();
            <span class="hljs-keyword">case</span> JSONNull ignored -&gt; <span class="hljs-number">4</span>; <span class="hljs-comment">// "null"</span>
        };
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-comment">// Build sample JSON structure</span>
        <span class="hljs-keyword">var</span> jsonData = <span class="hljs-keyword">new</span> JSONObject(java.util.Map.of(
            <span class="hljs-string">"name"</span>, <span class="hljs-keyword">new</span> JSONString(<span class="hljs-string">"John Doe"</span>),
            <span class="hljs-string">"age"</span>, <span class="hljs-keyword">new</span> JSONNumber(<span class="hljs-number">30</span>),
            <span class="hljs-string">"active"</span>, <span class="hljs-keyword">new</span> JSONBoolean(<span class="hljs-keyword">true</span>),
            <span class="hljs-string">"address"</span>, JSONNull.INSTANCE,
            <span class="hljs-string">"hobbies"</span>, <span class="hljs-keyword">new</span> JSONArray(java.util.List.of(
                <span class="hljs-keyword">new</span> JSONString(<span class="hljs-string">"reading"</span>),
                <span class="hljs-keyword">new</span> JSONString(<span class="hljs-string">"coding"</span>),
                <span class="hljs-keyword">new</span> JSONString(<span class="hljs-string">"gaming"</span>)
            ))
        ));

        System.out.println(<span class="hljs-string">"=== JSON Structure ==="</span>);
        System.out.println(jsonData.toJson());

        System.out.println(<span class="hljs-string">"\n=== Type Descriptions (Exhaustive Switch) ==="</span>);
        jsonData.values().forEach((key, value) -&gt; {
            System.out.println(key + <span class="hljs-string">" -&gt; "</span> + describeType(value));
        });

        System.out.println(<span class="hljs-string">"\n=== Size Estimation ==="</span>);
        System.out.println(<span class="hljs-string">"Estimated size: "</span> + estimateSize(jsonData) + <span class="hljs-string">" characters"</span>);
        System.out.println(<span class="hljs-string">"Actual size: "</span> + jsonData.toJson().length() + <span class="hljs-string">" characters"</span>);

        <span class="hljs-comment">// Demonstrate exhaustiveness</span>
        System.out.println(<span class="hljs-string">"\n=== Exhaustive Pattern Matching ==="</span>);
        <span class="hljs-keyword">var</span> values = java.util.List.of(
            <span class="hljs-keyword">new</span> JSONString(<span class="hljs-string">"test"</span>),
            <span class="hljs-keyword">new</span> JSONNumber(<span class="hljs-number">42</span>),
            <span class="hljs-keyword">new</span> JSONBoolean(<span class="hljs-keyword">false</span>),
            JSONNull.INSTANCE
        );

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> value : values) {
            System.out.println(describeType(value));
        }
    }
}
</code></pre>
<pre><code class="lang-java"><span class="hljs-comment">// Unit tests</span>

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create JSONString and convert to JSON")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateJSONStringAndConvertToJSON</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> json = <span class="hljs-keyword">new</span> JSONString(<span class="hljs-string">"hello"</span>);

    assertEquals(<span class="hljs-string">"\"hello\""</span>, json.toJson());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create JSONNumber and convert to JSON")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateJSONNumberAndConvertToJSON</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> json = <span class="hljs-keyword">new</span> JSONNumber(<span class="hljs-number">42.5</span>);

    assertEquals(<span class="hljs-string">"42.5"</span>, json.toJson());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create JSONBoolean and convert to JSON")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateJSONBooleanAndConvertToJSON</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> jsonTrue = <span class="hljs-keyword">new</span> JSONBoolean(<span class="hljs-keyword">true</span>);
    <span class="hljs-keyword">var</span> jsonFalse = <span class="hljs-keyword">new</span> JSONBoolean(<span class="hljs-keyword">false</span>);

    assertEquals(<span class="hljs-string">"true"</span>, jsonTrue.toJson());
    assertEquals(<span class="hljs-string">"false"</span>, jsonFalse.toJson());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create JSONNull and convert to JSON")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateJSONNullAndConvertToJSON</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> json = JSONNull.INSTANCE;

    assertEquals(<span class="hljs-string">"null"</span>, json.toJson());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create JSONArray and convert to JSON")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateJSONArrayAndConvertToJSON</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> json = <span class="hljs-keyword">new</span> JSONArray(java.util.List.of(
        <span class="hljs-keyword">new</span> JSONNumber(<span class="hljs-number">1</span>),
        <span class="hljs-keyword">new</span> JSONNumber(<span class="hljs-number">2</span>),
        <span class="hljs-keyword">new</span> JSONNumber(<span class="hljs-number">3</span>)
    ));

    assertEquals(<span class="hljs-string">"[1.0,2.0,3.0]"</span>, json.toJson());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should describe JSON types using exhaustive switch")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldDescribeJSONTypesUsingExhaustiveSwitch</span><span class="hljs-params">()</span> </span>{
    assertEquals(<span class="hljs-string">"string: \"test\""</span>,
        ExhaustiveSwitchExample.describeType(<span class="hljs-keyword">new</span> JSONString(<span class="hljs-string">"test"</span>)));
    assertEquals(<span class="hljs-string">"number: 42.0"</span>,
        ExhaustiveSwitchExample.describeType(<span class="hljs-keyword">new</span> JSONNumber(<span class="hljs-number">42</span>)));
    assertEquals(<span class="hljs-string">"boolean: true"</span>,
        ExhaustiveSwitchExample.describeType(<span class="hljs-keyword">new</span> JSONBoolean(<span class="hljs-keyword">true</span>)));
    assertEquals(<span class="hljs-string">"null value"</span>,
        ExhaustiveSwitchExample.describeType(JSONNull.INSTANCE));
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should estimate JSON size correctly")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldEstimateJSONSizeCorrectly</span><span class="hljs-params">()</span> </span>{
    assertEquals(<span class="hljs-number">6</span>, ExhaustiveSwitchExample.estimateSize(<span class="hljs-keyword">new</span> JSONString(<span class="hljs-string">"test"</span>))); <span class="hljs-comment">// "test"</span>
    assertEquals(<span class="hljs-number">4</span>, ExhaustiveSwitchExample.estimateSize(JSONNull.INSTANCE)); <span class="hljs-comment">// null</span>
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should verify JSONValue interface is sealed")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldVerifyJSONValueInterfaceIsSealed</span><span class="hljs-params">()</span> </span>{
    assertTrue(JSONValue.class.isSealed());
    assertEquals(<span class="hljs-number">6</span>, JSONValue.class.getPermittedSubclasses().length);
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">=== JSON Structure ===
{"hobbies":["reading","coding","gaming"],"address":null,"name":"John Doe","active":true,"age":30.0}

=== Type Descriptions (Exhaustive Switch) ===
hobbies -&gt; array with 3 elements
address -&gt; null value
name -&gt; string: "John Doe"
active -&gt; boolean: true
age -&gt; number: 30.0

=== Size Estimation ===
Estimated size: 88 characters
Actual size: 94 characters

=== Exhaustive Pattern Matching ===
string: "test"
number: 42.0
boolean: false
null value
</code></pre>
<p><strong>Key Insight</strong>: The compiler enforces exhaustiveness in switches. Adding a new type without updating all switches causes compilation errors.</p>
<hr />
<h3 id="heading-example-5-the-non-sealed-modifier">Example 5: The <code>non-sealed</code> Modifier</h3>
<p>The <code>non-sealed</code> modifier breaks the seal, allowing unrestricted subclassing from that point onward in the hierarchy.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Sealed base class</span>
<span class="hljs-keyword">public</span> sealed <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Animal</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">Mammal</span>, <span class="hljs-title">Bird</span>, <span class="hljs-title">Fish</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String name;

    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-title">Animal</span><span class="hljs-params">(String name)</span> </span>{
        <span class="hljs-keyword">this</span>.name = name;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">name</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> name; }
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">abstract</span> String <span class="hljs-title">sound</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-comment">// Sealed intermediate class - controlled hierarchy continues</span>
<span class="hljs-keyword">public</span> sealed <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Mammal</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Animal</span>
    <span class="hljs-title">permits</span> <span class="hljs-title">Dog</span>, <span class="hljs-title">Cat</span>, <span class="hljs-title">Pet</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-title">Mammal</span><span class="hljs-params">(String name)</span> </span>{
        <span class="hljs-keyword">super</span>(name);
    }
}

<span class="hljs-comment">// Non-sealed class - breaks the seal!</span>
<span class="hljs-keyword">public</span> non-sealed <span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Pet</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Mammal</span> </span>{
    <span class="hljs-keyword">private</span> String owner;

    <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-title">Pet</span><span class="hljs-params">(String name, String owner)</span> </span>{
        <span class="hljs-keyword">super</span>(name);
        <span class="hljs-keyword">this</span>.owner = owner;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">owner</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> owner; }
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setOwner</span><span class="hljs-params">(String owner)</span> </span>{ <span class="hljs-keyword">this</span>.owner = owner; }
}

<span class="hljs-comment">// Now anyone can extend Pet - the seal is broken</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Hamster</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Pet</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String color;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Hamster</span><span class="hljs-params">(String name, String owner, String color)</span> </span>{
        <span class="hljs-keyword">super</span>(name, owner);
        <span class="hljs-keyword">this</span>.color = color;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">color</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> color; }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sound</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"squeak squeak"</span>;
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GuineaPig</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Pet</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">boolean</span> isLongHaired;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">GuineaPig</span><span class="hljs-params">(String name, String owner, <span class="hljs-keyword">boolean</span> isLongHaired)</span> </span>{
        <span class="hljs-keyword">super</span>(name, owner);
        <span class="hljs-keyword">this</span>.isLongHaired = isLongHaired;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isLongHaired</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> isLongHaired; }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sound</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"wheek wheek"</span>;
    }
}

<span class="hljs-comment">// Final implementations in sealed hierarchy</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Dog</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Mammal</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String breed;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Dog</span><span class="hljs-params">(String name, String breed)</span> </span>{
        <span class="hljs-keyword">super</span>(name);
        <span class="hljs-keyword">this</span>.breed = breed;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">breed</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> breed; }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sound</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"woof"</span>;
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Cat</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Mammal</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">boolean</span> isIndoor;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Cat</span><span class="hljs-params">(String name, <span class="hljs-keyword">boolean</span> isIndoor)</span> </span>{
        <span class="hljs-keyword">super</span>(name);
        <span class="hljs-keyword">this</span>.isIndoor = isIndoor;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isIndoor</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> isIndoor; }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sound</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"meow"</span>;
    }
}

<span class="hljs-comment">// Other sealed branches</span>
<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Bird</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Animal</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">double</span> wingspan;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Bird</span><span class="hljs-params">(String name, <span class="hljs-keyword">double</span> wingspan)</span> </span>{
        <span class="hljs-keyword">super</span>(name);
        <span class="hljs-keyword">this</span>.wingspan = wingspan;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> <span class="hljs-title">wingspan</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> wingspan; }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sound</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"chirp"</span>;
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Fish</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Animal</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String waterType;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Fish</span><span class="hljs-params">(String name, String waterType)</span> </span>{
        <span class="hljs-keyword">super</span>(name);
        <span class="hljs-keyword">this</span>.waterType = waterType;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">waterType</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> waterType; }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">sound</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"blub"</span>;
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NonSealedExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-keyword">var</span> animals = java.util.List.of(
            <span class="hljs-keyword">new</span> Dog(<span class="hljs-string">"Buddy"</span>, <span class="hljs-string">"Golden Retriever"</span>),
            <span class="hljs-keyword">new</span> Cat(<span class="hljs-string">"Whiskers"</span>, <span class="hljs-keyword">true</span>),
            <span class="hljs-keyword">new</span> Bird(<span class="hljs-string">"Tweety"</span>, <span class="hljs-number">15.5</span>),
            <span class="hljs-keyword">new</span> Fish(<span class="hljs-string">"Nemo"</span>, <span class="hljs-string">"saltwater"</span>),
            <span class="hljs-keyword">new</span> Hamster(<span class="hljs-string">"Fluffy"</span>, <span class="hljs-string">"Alice"</span>, <span class="hljs-string">"brown"</span>),
            <span class="hljs-keyword">new</span> GuineaPig(<span class="hljs-string">"Patches"</span>, <span class="hljs-string">"Bob"</span>, <span class="hljs-keyword">true</span>)
        );

        System.out.println(<span class="hljs-string">"=== Animal Sounds ==="</span>);
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> animal : animals) {
            System.out.println(animal.name() + <span class="hljs-string">" says: "</span> + animal.sound());
        }

        <span class="hljs-comment">// Demonstrate non-sealed hierarchy</span>
        System.out.println(<span class="hljs-string">"\n=== Pet Hierarchy (non-sealed) ==="</span>);
        <span class="hljs-keyword">var</span> pets = animals.stream()
            .filter(a -&gt; a <span class="hljs-keyword">instanceof</span> Pet)
            .map(a -&gt; (Pet) a)
            .toList();

        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> pet : pets) {
            System.out.println(pet.name() + <span class="hljs-string">" belongs to "</span> + pet.owner());
        }

        <span class="hljs-comment">// Reflection on sealed hierarchy</span>
        System.out.println(<span class="hljs-string">"\n=== Sealed Hierarchy Analysis ==="</span>);
        System.out.println(<span class="hljs-string">"Animal is sealed: "</span> + Animal.class.isSealed());
        System.out.println(<span class="hljs-string">"Mammal is sealed: "</span> + Mammal.class.isSealed());
        System.out.println(<span class="hljs-string">"Pet is sealed: "</span> + Pet.class.isSealed()); <span class="hljs-comment">// false!</span>

        System.out.println(<span class="hljs-string">"\nMammal permits: "</span>);
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> permitted : Mammal.class.getPermittedSubclasses()) {
            System.out.println(<span class="hljs-string">"  - "</span> + permitted.getSimpleName());
        }
    }
}
</code></pre>
<pre><code class="lang-java"><span class="hljs-comment">// Unit tests</span>

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create Dog with breed information")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateDogWithBreedInformation</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> dog = <span class="hljs-keyword">new</span> Dog(<span class="hljs-string">"Buddy"</span>, <span class="hljs-string">"Golden Retriever"</span>);

    assertEquals(<span class="hljs-string">"Buddy"</span>, dog.name());
    assertEquals(<span class="hljs-string">"Golden Retriever"</span>, dog.breed());
    assertEquals(<span class="hljs-string">"woof"</span>, dog.sound());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create Cat with indoor status")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateCatWithIndoorStatus</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> cat = <span class="hljs-keyword">new</span> Cat(<span class="hljs-string">"Whiskers"</span>, <span class="hljs-keyword">true</span>);

    assertEquals(<span class="hljs-string">"Whiskers"</span>, cat.name());
    assertTrue(cat.isIndoor());
    assertEquals(<span class="hljs-string">"meow"</span>, cat.sound());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create Hamster as Pet subclass")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateHamsterAsPetSubclass</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> hamster = <span class="hljs-keyword">new</span> Hamster(<span class="hljs-string">"Fluffy"</span>, <span class="hljs-string">"Alice"</span>, <span class="hljs-string">"brown"</span>);

    assertEquals(<span class="hljs-string">"Fluffy"</span>, hamster.name());
    assertEquals(<span class="hljs-string">"Alice"</span>, hamster.owner());
    assertEquals(<span class="hljs-string">"brown"</span>, hamster.color());
    assertEquals(<span class="hljs-string">"squeak squeak"</span>, hamster.sound());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create GuineaPig as Pet subclass")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateGuineaPigAsPetSubclass</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> guineaPig = <span class="hljs-keyword">new</span> GuineaPig(<span class="hljs-string">"Patches"</span>, <span class="hljs-string">"Bob"</span>, <span class="hljs-keyword">true</span>);

    assertEquals(<span class="hljs-string">"Patches"</span>, guineaPig.name());
    assertEquals(<span class="hljs-string">"Bob"</span>, guineaPig.owner());
    assertTrue(guineaPig.isLongHaired());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should verify Animal and Mammal are sealed but Pet is not")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldVerifyAnimalAndMammalAreSealedButPetIsNot</span><span class="hljs-params">()</span> </span>{
    assertTrue(Animal.class.isSealed());
    assertTrue(Mammal.class.isSealed());
    assertFalse(Pet.class.isSealed()); // non-sealed!
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should allow unlimited extensions of non-sealed Pet class")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldAllowUnlimitedExtensionsOfNonSealedPetClass</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-comment">// Hamster and GuineaPig can extend Pet without being in permits clause</span>
    assertTrue(Hamster.class.getSuperclass().equals(Pet.class));
    assertTrue(GuineaPig.class.getSuperclass().equals(Pet.class));
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should update Pet owner")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldUpdatePetOwner</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> hamster = <span class="hljs-keyword">new</span> Hamster(<span class="hljs-string">"Fluffy"</span>, <span class="hljs-string">"Alice"</span>, <span class="hljs-string">"brown"</span>);
    assertEquals(<span class="hljs-string">"Alice"</span>, hamster.owner());

    hamster.setOwner(<span class="hljs-string">"Bob"</span>);
    assertEquals(<span class="hljs-string">"Bob"</span>, hamster.owner());
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">=== Animal Sounds ===
Buddy says: woof
Whiskers says: meow
Tweety says: chirp
Nemo says: blub
Fluffy says: squeak squeak
Patches says: wheek wheek

=== Pet Hierarchy (non-sealed) ===
Fluffy belongs to Alice
Patches belongs to Bob

=== Sealed Hierarchy Analysis ===
Animal is sealed: true
Mammal is sealed: true
Pet is sealed: false

Mammal permits:
  - Dog
  - Cat
  - Pet
</code></pre>
<p><strong>Key Insight</strong>: The <code>non-sealed</code> modifier strategically breaks the seal at a specific point in the hierarchy. <code>Animal</code> and <code>Mammal</code> remain sealed with controlled inheritance, but <code>Pet</code> allows unlimited extensions. This is useful when you want tight control over core abstractions but flexibility in specific branches—like allowing plugin developers to create custom pet types while keeping the core <code>Animal</code> hierarchy closed.</p>
<hr />
<h2 id="heading-best-practices">Best Practices</h2>
<h3 id="heading-when-to-use-sealed-classes">When to Use Sealed Classes</h3>
<p><strong>Use sealed classes when:</strong></p>
<ol>
<li><p><strong>Domain modeling with closed sets</strong> - Payment types, order statuses, geometric shapes, or any domain concept with a fixed set of variants</p>
</li>
<li><p><strong>Exhaustive pattern matching required</strong> - When you need the compiler to verify you've handled all cases</p>
</li>
<li><p><strong>API stability matters</strong> - Prevent external code from creating unexpected subtypes</p>
</li>
<li><p><strong>Type hierarchy is well-defined</strong> - You know all subtypes at design time and they won't change frequently</p>
</li>
</ol>
<p><strong>Don't use sealed classes when:</strong></p>
<ol>
<li><p><strong>Plugin architectures</strong> - If you need third-party extensions, use interfaces</p>
</li>
<li><p><strong>Frequently changing hierarchies</strong> - Adding new subtypes requires modifying the sealed parent</p>
</li>
<li><p><strong>Library APIs with backward compatibility concerns</strong> - Sealed classes can't be extended outside your module</p>
</li>
</ol>
<h3 id="heading-design-patterns-with-sealed-classes">Design Patterns with Sealed Classes</h3>
<p><strong>1. Algebraic Data Types (ADTs)</strong></p>
<p>Sealed classes with records support algebraic data types (ADTs):</p>
<pre><code class="lang-java">sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Expression</span> <span class="hljs-title">permits</span> <span class="hljs-title">Constant</span>, <span class="hljs-title">Addition</span>, <span class="hljs-title">Multiplication</span> </span>{}
<span class="hljs-function">record <span class="hljs-title">Constant</span><span class="hljs-params">(<span class="hljs-keyword">int</span> value)</span> implements Expression </span>{}
<span class="hljs-function">record <span class="hljs-title">Addition</span><span class="hljs-params">(Expression left, Expression right)</span> implements Expression </span>{}
<span class="hljs-function">record <span class="hljs-title">Multiplication</span><span class="hljs-params">(Expression left, Expression right)</span> implements Expression </span>{}
</code></pre>
<p><strong>2. State Machines</strong></p>
<p>Model state transitions explicitly:</p>
<pre><code class="lang-java">sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">OrderState</span> <span class="hljs-title">permits</span> <span class="hljs-title">Pending</span>, <span class="hljs-title">Confirmed</span>, <span class="hljs-title">Shipped</span>, <span class="hljs-title">Delivered</span>, <span class="hljs-title">Cancelled</span> </span>{}
<span class="hljs-comment">// Each state can have different properties and transitions</span>
</code></pre>
<p><strong>3. Result/Option Types</strong></p>
<p>Type-safe error handling and optional values:</p>
<pre><code class="lang-java">sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Result</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-title">permits</span> <span class="hljs-title">Success</span>, <span class="hljs-title">Failure</span> </span>{}
sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Option</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-title">permits</span> <span class="hljs-title">Some</span>, <span class="hljs-title">None</span> </span>{}
</code></pre>
<h3 id="heading-combining-with-other-features">Combining with Other Features</h3>
<p><strong>Sealed + Records</strong> - Immutable domain models with minimal boilerplate</p>
<pre><code class="lang-java">sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Vehicle</span> <span class="hljs-title">permits</span> <span class="hljs-title">Car</span>, <span class="hljs-title">Bike</span> </span>{}
<span class="hljs-function">record <span class="hljs-title">Car</span><span class="hljs-params">(String brand, <span class="hljs-keyword">int</span> doors)</span> implements Vehicle </span>{}
<span class="hljs-function">record <span class="hljs-title">Bike</span><span class="hljs-params">(String brand, <span class="hljs-keyword">int</span> gears)</span> implements Vehicle </span>{}
</code></pre>
<p><strong>Sealed + Pattern Matching</strong> - Exhaustive switches without <code>default</code></p>
<pre><code class="lang-java"><span class="hljs-keyword">return</span> <span class="hljs-keyword">switch</span> (vehicle) {
    <span class="hljs-keyword">case</span> Car c -&gt; <span class="hljs-string">"Car with "</span> + c.doors() + <span class="hljs-string">" doors"</span>;
    <span class="hljs-keyword">case</span> Bike b -&gt; <span class="hljs-string">"Bike with "</span> + b.gears() + <span class="hljs-string">" gears"</span>;
};
</code></pre>
<p><strong>Sealed + Enums</strong> - Enums are implicitly final, suitable for sealed types</p>
<pre><code class="lang-java">sealed <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">TrafficLight</span> <span class="hljs-title">permits</span> <span class="hljs-title">Red</span>, <span class="hljs-title">Yellow</span>, <span class="hljs-title">Green</span> </span>{}
<span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Red</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">TrafficLight</span> </span>{ INSTANCE }
<span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Yellow</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">TrafficLight</span> </span>{ INSTANCE }
<span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Green</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">TrafficLight</span> </span>{ INSTANCE }
</code></pre>
<h3 id="heading-package-organization">Package Organization</h3>
<p>Keep sealed classes and permitted subclasses in the same package:</p>
<pre><code class="lang-plaintext">com.example.domain/
  ├── Payment.java (sealed interface)
  ├── CreditCard.java (final record)
  ├── DebitCard.java (final record)
  └── Cash.java (final record)
</code></pre>
<p>For large hierarchies, use subpackages:</p>
<pre><code class="lang-plaintext">com.example.shapes/
  ├── Shape.java (sealed)
  ├── circle/
  │   └── Circle.java
  ├── polygon/
  │   ├── Polygon.java (sealed)
  │   ├── Triangle.java
  │   └── Rectangle.java
  └── freeform/
      └── FreeformShape.java (non-sealed)
</code></pre>
<hr />
<h2 id="heading-pitfalls-and-limitations">Pitfalls and Limitations</h2>
<h3 id="heading-common-mistakes">Common Mistakes</h3>
<p><strong>1. Forgetting the subclass modifier</strong></p>
<pre><code class="lang-java">sealed <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Animal</span> <span class="hljs-title">permits</span> <span class="hljs-title">Dog</span>, <span class="hljs-title">Cat</span> </span>{}

<span class="hljs-comment">// ❌ Compile error - must use final, sealed, or non-sealed</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Dog</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Animal</span> </span>{}

<span class="hljs-comment">// ✅ Correct</span>
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Dog</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Animal</span> </span>{}
</code></pre>
<p><strong>2. Subclass in wrong package</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">package</span> com.example.domain;
sealed <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Payment</span> <span class="hljs-title">permits</span> <span class="hljs-title">CreditCard</span> </span>{}

<span class="hljs-keyword">package</span> com.example.impl; <span class="hljs-comment">// ❌ Different package!</span>
<span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CreditCard</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Payment</span> </span>{} <span class="hljs-comment">// Compile error</span>
</code></pre>
<p><strong>3. Using</strong> <code>var</code> with exhaustive switch</p>
<pre><code class="lang-java"><span class="hljs-comment">// ❌ Doesn't compile - var doesn't work with pattern matching</span>
<span class="hljs-keyword">var</span> result = <span class="hljs-keyword">switch</span> (shape) {
    <span class="hljs-keyword">case</span> <span class="hljs-keyword">var</span> c when c <span class="hljs-keyword">instanceof</span> Circle -&gt; <span class="hljs-string">"circle"</span>;
    <span class="hljs-comment">// ...</span>
};

<span class="hljs-comment">// ✅ Use explicit type patterns</span>
<span class="hljs-keyword">var</span> result = <span class="hljs-keyword">switch</span> (shape) {
    <span class="hljs-keyword">case</span> Circle c -&gt; <span class="hljs-string">"circle"</span>;
    <span class="hljs-keyword">case</span> Rectangle r -&gt; <span class="hljs-string">"rectangle"</span>;
    <span class="hljs-comment">// ...</span>
};
</code></pre>
<h3 id="heading-performance-considerations">Performance Considerations</h3>
<p><strong>Memory overhead</strong> - Sealed classes have the same memory footprint as regular classes. No additional overhead.</p>
<p><strong>instanceof checks</strong> - Pattern matching with sealed types is <strong>fast</strong>. The JVM can optimize switches over sealed hierarchies because it knows all possible types.</p>
<p><strong>Reflection</strong> - <code>Class.isSealed()</code> and <code>getPermittedSubclasses()</code> have negligible overhead. Use freely.</p>
<h3 id="heading-limitations">Limitations</h3>
<p><strong>1. Cannot seal classes from other modules</strong></p>
<p>You can't make <code>java.lang.String</code> sealed—you don't own it.</p>
<p><strong>2. No runtime enforcement</strong></p>
<p>Sealed classes are compile-time only. Reflection can still create proxies or use <code>Unsafe</code> to bypass restrictions (but don't do this).</p>
<p><strong>3. Limited to same module/package</strong></p>
<p>In JPMS (Java Platform Module System), sealed classes and subclasses must be in the same module. For non-modular projects, they must be in the same package.</p>
<p><strong>4. Cannot change permits clause without breaking changes</strong></p>
<p>Adding or removing permitted subclasses is a breaking change for anyone using exhaustive switches.</p>
<h3 id="heading-migration-strategies">Migration Strategies</h3>
<p><strong>From open hierarchies:</strong></p>
<ol>
<li><p>Identify all current subclasses</p>
</li>
<li><p>Add <code>sealed</code> and <code>permits</code> to parent</p>
</li>
<li><p>Add <code>final</code>, <code>sealed</code>, or <code>non-sealed</code> to all subclasses</p>
</li>
<li><p>Search for switch statements and remove <code>default</code> where appropriate</p>
</li>
</ol>
<p><strong>From final classes:</strong></p>
<ul>
<li><p>If you have a <code>final</code> class and want to add subtypes, change to <code>sealed</code> with explicit <code>permits</code></p>
</li>
<li><p>This is non-breaking for existing code</p>
</li>
</ul>
<hr />
<h2 id="heading-summary-and-next-steps">Summary and Next Steps</h2>
<p>In this article, we explored <strong>Sealed Classes</strong> (<a target="_blank" href="https://openjdk.org/jeps/409">JEP 409</a>), one of Java 17's most powerful features for domain modeling and type safety.</p>
<p><strong>What we covered:</strong></p>
<ul>
<li><p><strong>The problem</strong>: Uncontrolled inheritance creates maintenance burdens and prevents compiler verification</p>
</li>
<li><p><strong>Sealed classes solution</strong>: Explicitly list permitted subclasses with the <code>permits</code> clause</p>
</li>
<li><p><strong>Three subclass modifiers</strong>: <code>final</code> (no extensions), <code>sealed</code> (controlled extensions), <code>non-sealed</code> (open extensions)</p>
</li>
<li><p><strong>Exhaustive pattern matching</strong>: Compiler-verified switches without <code>default</code> cases</p>
</li>
<li><p><strong>Multi-level hierarchies</strong>: Sealed classes at multiple levels for complex domain models</p>
</li>
<li><p><strong>Practical patterns</strong>: Result types, algebraic data types, state machines</p>
</li>
</ul>
<p><strong>When to use sealed classes:</strong></p>
<ul>
<li><p>Domain modeling with closed sets (payment types, shapes, order states)</p>
</li>
<li><p>APIs where you control all implementations</p>
</li>
<li><p>Type hierarchies that benefit from exhaustive checking</p>
</li>
</ul>
<p><strong>When to avoid sealed classes:</strong></p>
<ul>
<li><p>Plugin architectures requiring third-party extensions</p>
</li>
<li><p>Frequently changing hierarchies</p>
</li>
<li><p>Library APIs with strict backward compatibility requirements</p>
</li>
</ul>
<hr />
<h3 id="heading-whats-next">What's Next?</h3>
<p>In <strong>Part 4</strong>, we'll explore <strong>Pattern Matching</strong> and <strong>Switch Expressions</strong>—two features that work with sealed classes. You'll learn:</p>
<ul>
<li><p>Pattern matching for <code>instanceof</code> (<a target="_blank" href="https://openjdk.org/jeps/394">JEP 394</a>)</p>
</li>
<li><p>Modern switch expressions with arrow syntax (<a target="_blank" href="https://openjdk.org/jeps/361">JEP 361</a>)</p>
</li>
<li><p>Guards and pattern combinations</p>
</li>
<li><p>Exhaustive switches with sealed types</p>
</li>
<li><p>Practical examples combining all three features</p>
</li>
</ul>
<p>Pattern matching and sealed classes together bring functional programming concepts to Java, enabling expressive, type-safe code with minimal boilerplate.</p>
<p><strong>All code examples from this article are available in the</strong> <a target="_blank" href="https://github.com/dawid-swist/blog-9mac-dev-code/"><strong>GitHub repository</strong></a><strong>:</strong></p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> https://github.com/dawid-swist/blog-9mac-dev-code.git
<span class="hljs-built_in">cd</span> blog-post-examples/java/2025-10-25-java17-features-every-senior-developer-should-know
../../gradlew <span class="hljs-built_in">test</span>
</code></pre>
<p>See you in Part 4, where we'll unlock the full power of sealed classes with pattern matching!</p>
<hr />
<p><em>This article is part of the "Java 17 Features Every Senior Developer Should Know" series. Check out</em> <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-1-introduction-and-var-keyword"><em>Part 1: Introduction &amp; var</em></a> <em>and</em> <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-2-records"><em>Part 2: Records</em></a> <em>if you haven't already.</em></p>
<p><strong>Previous</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-2-records">Part 2 - Part 2: Records</a> <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-4-pattern-matching-and-switch-expressions?showSharer=true#heading-additional-reading"><strong>Next</strong>: Part 4</a> - Pattern Matching &amp; Switch Expressions</p>
]]></content:encoded></item><item><title><![CDATA[Java 17 Features Every Senior Developer Should Know - Part 2: Records]]></title><description><![CDATA[Java 17 Features Every Senior Developer Should Know - Part 2: Records
Welcome back to our comprehensive series on Java 17 features! In Part 1, we explored the var keyword and how type inference reduces boilerplate in local variable declarations. Toda...]]></description><link>https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-2-records</link><guid isPermaLink="true">https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-2-records</guid><category><![CDATA[Java]]></category><category><![CDATA[java 17]]></category><dc:creator><![CDATA[Dawid Świst]]></dc:creator><pubDate>Sat, 08 Nov 2025 23:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1762282653423/905ada45-5894-412f-98e5-bc81b67451ae.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Java 17 Features Every Senior Developer Should Know - Part 2: Records</p>
<p>Welcome back to our comprehensive series on Java 17 features! In Part 1, we explored the <code>var</code> keyword and how type inference reduces boilerplate in local variable declarations. Today, we're diving into one of Java's most impactful features: <strong>Records</strong> (<a target="_blank" href="https://openjdk.org/jeps/395">JEP 395</a>).</p>
<p>If you've ever groaned while writing yet another data class with getters, equals, hashCode, and toString methods, this article is for you. Records eliminate this ceremony entirely, letting you declare data carriers in a single line while the compiler handles the rest.</p>
<hr />
<h2 id="heading-the-problem-boilerplate-in-data-classes">The Problem: Boilerplate in Data Classes</h2>
<p>Before Java 16, creating a simple data class meant writing dozens of lines of repetitive code. Let's look at a typical example - a <code>Point</code> class representing coordinates:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Point</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> x;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> y;

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{
        <span class="hljs-keyword">this</span>.x = x;
        <span class="hljs-keyword">this</span>.y = y;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getX</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> x; }
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getY</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> y; }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">equals</span><span class="hljs-params">(Object o)</span> </span>{
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">this</span> == o) <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
        <span class="hljs-keyword">if</span> (o == <span class="hljs-keyword">null</span> || getClass() != o.getClass()) <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
        Point point = (Point) o;
        <span class="hljs-keyword">return</span> x == point.x &amp;&amp; y == point.y;
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">hashCode</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> Objects.hash(x, y);
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Point[x="</span> + x + <span class="hljs-string">", y="</span> + y + <span class="hljs-string">"]"</span>;
    }
}
</code></pre>
<p>That's <strong>30+ lines of code</strong> just to store two integers! And the problems don't stop there:</p>
<ul>
<li><p><strong>Error-prone</strong>: Forget to update <code>equals()</code> when adding a field? Your tests will miss subtle bugs.</p>
</li>
<li><p><strong>Maintenance burden</strong>: Every field addition means updating multiple methods.</p>
</li>
<li><p><strong>Cluttered code</strong>: The ceremony obscures the intent: "this class holds x and y coordinates."</p>
</li>
<li><p><strong>Refactoring friction</strong>: Renaming fields requires changes in 4-5 places.</p>
</li>
</ul>
<h3 id="heading-the-lombok-compromise">The Lombok Compromise</h3>
<p>Many teams turned to Project Lombok as a temporary solution:</p>
<pre><code class="lang-java"><span class="hljs-meta">@Value</span> <span class="hljs-comment">// Generates immutable class with all boilerplate</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Point</span> </span>{
    <span class="hljs-keyword">int</span> x;
    <span class="hljs-keyword">int</span> y;
}
</code></pre>
<p>Lombok works, but it has downsides:</p>
<ul>
<li><p>Requires IDE plugins and annotation processors</p>
</li>
<li><p>Generated code is invisible in source form</p>
</li>
<li><p>Not part of the Java language specification</p>
</li>
<li><p>Can cause build tool complications</p>
</li>
<li><p>Creates team friction ("Do we really need another dependency?")</p>
</li>
</ul>
<p>The Java community needed a <strong>native language solution</strong>. Enter Records.</p>
<hr />
<h2 id="heading-what-are-records">What Are Records?</h2>
<p>A <strong>record</strong> is a special kind of class designed to be a transparent carrier for immutable data. When you declare a record, the compiler automatically generates:</p>
<ol>
<li><p><strong>Private final fields</strong> for each component</p>
</li>
<li><p><strong>Public accessor methods</strong> (not getters - just the component name)</p>
</li>
<li><p><strong>A canonical constructor</strong> accepting all components</p>
</li>
<li><p><strong>Proper</strong> <code>equals()</code> method comparing all components</p>
</li>
<li><p><strong>Proper</strong> <code>hashCode()</code> method combining all components</p>
</li>
<li><p><strong>A</strong> <code>toString()</code> method with record-specific format</p>
</li>
</ol>
<p>Here's how the <code>Point</code> class looks as a record:</p>
<pre><code class="lang-java"><span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{
}
</code></pre>
<p>That's it. <strong>One line.</strong> The compiler generates the equivalent of all 30+ lines we wrote manually.</p>
<h3 id="heading-key-characteristics">Key Characteristics</h3>
<p>Records have specific properties that distinguish them from regular classes:</p>
<ul>
<li><p><strong>Immutability</strong>: All components are implicitly <code>final</code></p>
</li>
<li><p><strong>Transparency</strong>: The state is part of the public API contract</p>
</li>
<li><p><strong>Nominal typing</strong>: Each record declaration creates a distinct type</p>
</li>
<li><p><strong>Restrictions</strong>: Records cannot extend other classes (they implicitly extend <code>java.lang.Record</code>)</p>
</li>
<li><p><strong>Final</strong>: Records are implicitly final - they cannot be subclassed</p>
</li>
</ul>
<hr />
<h2 id="heading-history-of-records">History of Records</h2>
<p>Records didn't appear overnight. They evolved through multiple Java versions based on community feedback.</p>
<h3 id="heading-java-14-preview-feature-jep-359">Java 14: Preview Feature (JEP 359)</h3>
<p>Records debuted in March 2020 as a <strong>preview feature</strong>, requiring the <code>--enable-preview</code> compiler flag. The basic syntax was introduced:</p>
<pre><code class="lang-java"><span class="hljs-function">record <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{}
</code></pre>
<p><strong>What you got:</strong></p>
<ul>
<li><p>Canonical constructor</p>
</li>
<li><p>Accessor methods (<code>p.x()</code>, not <code>p.getX()</code>)</p>
</li>
<li><p><code>equals()</code>, <code>hashCode()</code>, <code>toString()</code> implementations</p>
</li>
<li><p>Ability to add custom methods</p>
</li>
</ul>
<p><strong>Limitations:</strong></p>
<ul>
<li><p>Preview status meant production use required caution</p>
</li>
<li><p>Limited to top-level declarations</p>
</li>
<li><p>Couldn't implement interfaces (initially)</p>
</li>
</ul>
<h3 id="heading-java-15-second-preview-jep-384">Java 15: Second Preview (JEP 384)</h3>
<p>September 2020 brought refinements based on developer feedback:</p>
<p><strong>New capabilities:</strong></p>
<ul>
<li><p><strong>Local records</strong> - declare records inside methods</p>
</li>
<li><p><strong>Compact constructor syntax</strong> - validation without repeating parameters</p>
</li>
<li><p><strong>Interface implementation</strong> - records can now implement interfaces</p>
</li>
<li><p><strong>Nested records</strong> - records within classes or other records</p>
</li>
</ul>
<pre><code class="lang-java"><span class="hljs-comment">// Local record - new in Java 15</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">processOrders</span><span class="hljs-params">(List&lt;Order&gt; orders)</span> </span>{
    <span class="hljs-function">record <span class="hljs-title">OrderSummary</span><span class="hljs-params">(String customer, <span class="hljs-keyword">double</span> total)</span> </span>{}

    <span class="hljs-keyword">var</span> summaries = orders.stream()
        .map(o -&gt; <span class="hljs-keyword">new</span> OrderSummary(o.customer(), o.total()))
        .toList();
}
</code></pre>
<h3 id="heading-java-16-standard-feature-jep-395">Java 16: Standard Feature (JEP 395)</h3>
<p>March 2021 marked the graduation of records to a <strong>standard, permanent feature</strong>. No more <code>--enable-preview</code> flag needed.</p>
<p><strong>Additional enhancements:</strong></p>
<ul>
<li><p><strong>Generic records</strong> - <code>record Pair&lt;T, U&gt;(T first, U second) {}</code></p>
</li>
<li><p><strong>Static members</strong> - static fields and methods allowed</p>
</li>
<li><p><strong>Annotation support</strong> - full annotation capabilities</p>
</li>
<li><p><strong>Reflection API</strong> - <code>Class.isRecord()</code>, <code>getRecordComponents()</code></p>
</li>
</ul>
<hr />
<h2 id="heading-basic-syntax-and-features">Basic Syntax and Features</h2>
<h3 id="heading-declaration-syntax">Declaration Syntax</h3>
<p>The basic record declaration consists of:</p>
<ol>
<li><p>The <code>record</code> keyword</p>
</li>
<li><p>The record name</p>
</li>
<li><p>Component list in parentheses (the <strong>header</strong>)</p>
</li>
<li><p>Optional body in curly braces</p>
</li>
</ol>
<pre><code class="lang-java"><span class="hljs-comment">// Minimal record - just the header</span>
<span class="hljs-function">record <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{}

<span class="hljs-comment">// Record with validation</span>
<span class="hljs-function">record <span class="hljs-title">Range</span><span class="hljs-params">(<span class="hljs-keyword">int</span> start, <span class="hljs-keyword">int</span> end)</span> </span>{
    <span class="hljs-comment">// Compact constructor</span>
    <span class="hljs-keyword">public</span> Range {
        <span class="hljs-keyword">if</span> (start &gt; end) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(
                <span class="hljs-string">"start ("</span> + start + <span class="hljs-string">") must not exceed end ("</span> + end + <span class="hljs-string">")"</span>
            );
        }
    }
}
</code></pre>
<h3 id="heading-what-the-compiler-generates">What the Compiler Generates</h3>
<p>When you declare <code>record Point(int x, int y) {}</code>, the compiler creates:</p>
<pre><code class="lang-java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Point</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Record</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> x;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> y;

    <span class="hljs-comment">// Canonical constructor</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{
        <span class="hljs-keyword">this</span>.x = x;
        <span class="hljs-keyword">this</span>.y = y;
    }

    <span class="hljs-comment">// Accessor methods (NOT getters!)</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">x</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> x; }
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">y</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> y; }

    <span class="hljs-comment">// equals comparing all components</span>
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">equals</span><span class="hljs-params">(Object obj)</span> </span>{
        <span class="hljs-keyword">return</span> obj <span class="hljs-keyword">instanceof</span> Point other &amp;&amp;
               x == other.x &amp;&amp;
               y == other.y;
    }

    <span class="hljs-comment">// hashCode combining all components</span>
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">hashCode</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> Objects.hash(x, y);
    }

    <span class="hljs-comment">// toString with record-specific format</span>
    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Point[x="</span> + x + <span class="hljs-string">", y="</span> + y + <span class="hljs-string">"]"</span>;
    }
}
</code></pre>
<h3 id="heading-canonical-constructor">Canonical Constructor</h3>
<p>The <strong>canonical constructor</strong> is the constructor that takes all components in order. The compiler generates it automatically:</p>
<pre><code class="lang-java"><span class="hljs-function">record <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{}

<span class="hljs-comment">// Compiler generates:</span>
<span class="hljs-comment">// public Point(int x, int y) { ... }</span>
</code></pre>
<p>You can provide your own canonical constructor if needed:</p>
<pre><code class="lang-java"><span class="hljs-function">record <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{
    <span class="hljs-comment">// Explicit canonical constructor</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{
        <span class="hljs-keyword">if</span> (x &lt; <span class="hljs-number">0</span> || y &lt; <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Coordinates must be non-negative"</span>);
        }
        <span class="hljs-keyword">this</span>.x = x;
        <span class="hljs-keyword">this</span>.y = y;
    }
}
</code></pre>
<h3 id="heading-compact-constructor">Compact Constructor</h3>
<p>Java 15 introduced the <strong>compact constructor</strong> - a more concise way to customize the canonical constructor:</p>
<pre><code class="lang-java"><span class="hljs-function">record <span class="hljs-title">Range</span><span class="hljs-params">(<span class="hljs-keyword">int</span> start, <span class="hljs-keyword">int</span> end)</span> </span>{
    <span class="hljs-comment">// Compact constructor - no parameter list!</span>
    <span class="hljs-keyword">public</span> Range {
        <span class="hljs-comment">// Validation runs BEFORE field initialization</span>
        <span class="hljs-keyword">if</span> (start &gt; end) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Invalid range"</span>);
        }
        <span class="hljs-comment">// Field assignments happen automatically after this block</span>
    }
}
</code></pre>
<p>The compact constructor is syntactic sugar. The compiler transforms it into the canonical constructor, inserting field assignments at the end.</p>
<p><strong>You can also normalize data:</strong></p>
<pre><code class="lang-java"><span class="hljs-function">record <span class="hljs-title">Person</span><span class="hljs-params">(String name, <span class="hljs-keyword">int</span> age)</span> </span>{
    <span class="hljs-keyword">public</span> Person {
        <span class="hljs-comment">// Normalize before storing</span>
        name = name.trim().toUpperCase();
        age = Math.max(<span class="hljs-number">0</span>, age); <span class="hljs-comment">// Ensure non-negative</span>
    }
}
</code></pre>
<h3 id="heading-custom-methods">Custom Methods</h3>
<p>Records can have instance and static methods:</p>
<pre><code class="lang-java"><span class="hljs-function">record <span class="hljs-title">Rectangle</span><span class="hljs-params">(<span class="hljs-keyword">int</span> width, <span class="hljs-keyword">int</span> height)</span> </span>{
    <span class="hljs-comment">// Instance method</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">area</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> width * height;
    }

    <span class="hljs-comment">// Static factory method</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Rectangle <span class="hljs-title">square</span><span class="hljs-params">(<span class="hljs-keyword">int</span> side)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Rectangle(side, side);
    }

    <span class="hljs-comment">// "Wither" method - returns modified copy</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Rectangle <span class="hljs-title">withWidth</span><span class="hljs-params">(<span class="hljs-keyword">int</span> newWidth)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Rectangle(newWidth, height);
    }
}
</code></pre>
<hr />
<h2 id="heading-practical-examples">Practical Examples</h2>
<p>Let's explore records through six practical examples, each with complete code and JUnit tests.</p>
<h3 id="heading-example-1-basic-records-with-validation">Example 1: Basic Records with Validation</h3>
<p>Records provide immutable data carriers with minimal boilerplate. The compiler automatically generates equals(), hashCode(), toString(), and accessor methods.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Basic record representing 2D coordinates</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{
}

<span class="hljs-comment">// Record with validation using compact constructor</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Range</span><span class="hljs-params">(<span class="hljs-keyword">int</span> start, <span class="hljs-keyword">int</span> end)</span> </span>{
    <span class="hljs-keyword">public</span> Range {
        <span class="hljs-keyword">if</span> (start &gt; end) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(
                <span class="hljs-string">"start must not exceed end: start="</span> + start + <span class="hljs-string">", end="</span> + end
            );
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">contains</span><span class="hljs-params">(<span class="hljs-keyword">int</span> value)</span> </span>{
        <span class="hljs-keyword">return</span> value &gt;= start &amp;&amp; value &lt;= end;
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BasicRecordExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-comment">// Point examples</span>
        <span class="hljs-keyword">var</span> p1 = <span class="hljs-keyword">new</span> Point(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>);
        <span class="hljs-keyword">var</span> p2 = <span class="hljs-keyword">new</span> Point(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>);
        <span class="hljs-keyword">var</span> p3 = <span class="hljs-keyword">new</span> Point(<span class="hljs-number">30</span>, <span class="hljs-number">40</span>);

        System.out.println(<span class="hljs-string">"Point p1: "</span> + p1);
        System.out.println(<span class="hljs-string">"Accessing components: x="</span> + p1.x() + <span class="hljs-string">", y="</span> + p1.y());

        <span class="hljs-comment">// Equality (value-based)</span>
        System.out.println(<span class="hljs-string">"p1.equals(p2): "</span> + p1.equals(p2));  <span class="hljs-comment">// true</span>
        System.out.println(<span class="hljs-string">"p1 == p2: "</span> + (p1 == p2));           <span class="hljs-comment">// false</span>
        System.out.println(<span class="hljs-string">"p1.hashCode() == p2.hashCode(): "</span> + (p1.hashCode() == p2.hashCode())); <span class="hljs-comment">// true</span>

        <span class="hljs-comment">// Range examples</span>
        <span class="hljs-keyword">var</span> r1 = <span class="hljs-keyword">new</span> Range(<span class="hljs-number">1</span>, <span class="hljs-number">10</span>);
        System.out.println(<span class="hljs-string">"\nRange: "</span> + r1);
        System.out.println(<span class="hljs-string">"Contains 5? "</span> + r1.contains(<span class="hljs-number">5</span>));
        System.out.println(<span class="hljs-string">"Contains 15? "</span> + r1.contains(<span class="hljs-number">15</span>));

        <span class="hljs-comment">// Validation</span>
        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">var</span> invalid = <span class="hljs-keyword">new</span> Range(<span class="hljs-number">10</span>, <span class="hljs-number">1</span>);
        } <span class="hljs-keyword">catch</span> (IllegalArgumentException e) {
            System.out.println(<span class="hljs-string">"Expected error: "</span> + e.getMessage());
        }
    }
}
</code></pre>
<pre><code class="lang-java"><span class="hljs-comment">// Unit tests</span>

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create Point with x and y coordinates")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreatePointWithCoordinates</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> point = <span class="hljs-keyword">new</span> Point(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>);

    assertEquals(<span class="hljs-number">10</span>, point.x());
    assertEquals(<span class="hljs-number">20</span>, point.y());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should compare Points by value equality")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldComparePointsByValueEquality</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> p1 = <span class="hljs-keyword">new</span> Point(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>);
    <span class="hljs-keyword">var</span> p2 = <span class="hljs-keyword">new</span> Point(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>);
    <span class="hljs-keyword">var</span> p3 = <span class="hljs-keyword">new</span> Point(<span class="hljs-number">10</span>, <span class="hljs-number">30</span>);

    assertEquals(p1, p2);
    assertNotEquals(p1, p3);
    assertEquals(p1.hashCode(), p2.hashCode());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should generate toString in record format")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldGenerateToStringInRecordFormat</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> point = <span class="hljs-keyword">new</span> Point(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>);

    assertEquals(<span class="hljs-string">"Point[x=10, y=20]"</span>, point.toString());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should throw exception when creating Range with start greater than end")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldThrowExceptionWhenCreatingRangeWithStartGreaterThanEnd</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> exception = assertThrows(
        IllegalArgumentException.class,
        () -&gt; <span class="hljs-keyword">new</span> Range(<span class="hljs-number">10</span>, <span class="hljs-number">1</span>)
    );

    assertTrue(exception.getMessage().contains(<span class="hljs-string">"start must not exceed end"</span>));
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">Point p1: Point[x=10, y=20]
Accessing components: x=10, y=20
p1.equals(p2): true
p1 == p2: false
p1.hashCode() == p2.hashCode(): true

Range: Range[start=1, end=10]
Contains 5? true
Contains 15? false
Expected error: start must not exceed end: start=10, end=1
</code></pre>
<p><strong>Key Insight</strong>: Records provide value semantics out of the box. Two <code>Point</code> instances with the same coordinates are considered equal, making them perfect for map keys and set elements.</p>
<h3 id="heading-example-2-generic-records">Example 2: Generic Records</h3>
<p>Generic records enable reusable, type-safe data structures with full type inference support.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Generic pair - holds two values of potentially different types</span>
<span class="hljs-keyword">public</span> record Pair&lt;T, U&gt;(T first, U second) {
    <span class="hljs-function"><span class="hljs-keyword">public</span> Pair&lt;U, T&gt; <span class="hljs-title">swap</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Pair&lt;&gt;(second, first);
    }
}

<span class="hljs-comment">// Generic record with bounded type parameter - only Number subtypes allowed</span>
<span class="hljs-keyword">public</span> record Box&lt;T extends Number&gt;(T value) {
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> <span class="hljs-title">doubleValue</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> value.doubleValue();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Box&lt;Integer&gt; <span class="hljs-title">ofInt</span><span class="hljs-params">(<span class="hljs-keyword">int</span> value)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Box&lt;&gt;(value);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GenericRecordExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-comment">// Pair with different types</span>
        <span class="hljs-keyword">var</span> pair1 = <span class="hljs-keyword">new</span> Pair&lt;&gt;(<span class="hljs-string">"age"</span>, <span class="hljs-number">30</span>);
        System.out.println(<span class="hljs-string">"String-Integer pair: "</span> + pair1);
        System.out.println(<span class="hljs-string">"First: "</span> + pair1.first() + <span class="hljs-string">", Second: "</span> + pair1.second());

        <span class="hljs-comment">// Swap elements</span>
        <span class="hljs-keyword">var</span> swapped = pair1.swap();
        System.out.println(<span class="hljs-string">"Swapped: "</span> + swapped);

        <span class="hljs-comment">// Box with bounded type</span>
        <span class="hljs-keyword">var</span> intBox = Box.ofInt(<span class="hljs-number">42</span>);
        <span class="hljs-keyword">var</span> doubleBox = <span class="hljs-keyword">new</span> Box&lt;&gt;(<span class="hljs-number">3.14</span>);
        System.out.println(<span class="hljs-string">"Integer box: "</span> + intBox);
        System.out.println(<span class="hljs-string">"  as double: "</span> + intBox.doubleValue());
        System.out.println(<span class="hljs-string">"Double box: "</span> + doubleBox);

        <span class="hljs-comment">// Nested generics</span>
        <span class="hljs-keyword">var</span> nested = <span class="hljs-keyword">new</span> Pair&lt;&gt;(<span class="hljs-keyword">new</span> Pair&lt;&gt;(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>), <span class="hljs-string">"coordinates"</span>);
        System.out.println(<span class="hljs-string">"Nested pair: "</span> + nested);
        System.out.println(<span class="hljs-string">"Inner first: "</span> + nested.first().first());
    }
}
</code></pre>
<pre><code class="lang-java"><span class="hljs-comment">// Unit tests</span>

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create generic Pair with different types")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateGenericPairWithDifferentTypes</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> p1 = <span class="hljs-keyword">new</span> Pair&lt;&gt;(<span class="hljs-string">"age"</span>, <span class="hljs-number">30</span>);
    assertEquals(<span class="hljs-string">"age"</span>, p1.first());
    assertEquals(<span class="hljs-number">30</span>, p1.second());

    <span class="hljs-keyword">var</span> p2 = <span class="hljs-keyword">new</span> Pair&lt;&gt;(<span class="hljs-number">42</span>, <span class="hljs-string">"answer"</span>);
    assertEquals(<span class="hljs-number">42</span>, p2.first());
    assertEquals(<span class="hljs-string">"answer"</span>, p2.second());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should swap Pair elements")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldSwapPairElements</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> original = <span class="hljs-keyword">new</span> Pair&lt;&gt;(<span class="hljs-string">"key"</span>, <span class="hljs-number">123</span>);
    <span class="hljs-keyword">var</span> swapped = original.swap();

    assertEquals(<span class="hljs-number">123</span>, swapped.first());
    assertEquals(<span class="hljs-string">"key"</span>, swapped.second());
    assertEquals(<span class="hljs-string">"key"</span>, original.first());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create Box with bounded generic type")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateBoxWithBoundedGenericType</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> intBox = <span class="hljs-keyword">new</span> Box&lt;&gt;(<span class="hljs-number">42</span>);
    <span class="hljs-keyword">var</span> doubleBox = <span class="hljs-keyword">new</span> Box&lt;&gt;(<span class="hljs-number">3.14</span>);

    assertEquals(<span class="hljs-number">42.0</span>, intBox.doubleValue());
    assertEquals(<span class="hljs-number">3.14</span>, doubleBox.doubleValue(), <span class="hljs-number">0.001</span>);
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should support nested Pairs")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldSupportNestedPairs</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> nested = <span class="hljs-keyword">new</span> Pair&lt;&gt;(<span class="hljs-keyword">new</span> Pair&lt;&gt;(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>), <span class="hljs-string">"coordinates"</span>);

    assertEquals(<span class="hljs-number">1</span>, nested.first().first());
    assertEquals(<span class="hljs-number">2</span>, nested.first().second());
    assertEquals(<span class="hljs-string">"coordinates"</span>, nested.second());
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">String-Integer pair: Pair[first=age, second=30]
First: age, Second: 30
Swapped: Pair[first=30, second=age]
Integer box: Box[value=42]
  as double: 42.0
Double box: Box[value=3.14]
Nested pair: Pair[first=Pair[first=1, second=2], second=coordinates]
Inner first: 1
</code></pre>
<p><strong>Key Insight</strong>: Generic records combine type safety with reusability. The bounded type parameter in <code>Box&lt;T extends Number&gt;</code> ensures only numeric types can be stored while still providing generic flexibility.</p>
<h3 id="heading-example-3-records-with-custom-methods">Example 3: Records with Custom Methods</h3>
<p>Records can contain rich behavior while maintaining immutability. The "wither" pattern allows creating modified copies.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Person record with custom methods and validation</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Person</span><span class="hljs-params">(String firstName, String lastName, <span class="hljs-keyword">int</span> age)</span> </span>{
    <span class="hljs-keyword">public</span> Person {
        firstName = firstName.trim();
        lastName = lastName.trim();
        <span class="hljs-keyword">if</span> (age &lt; <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Age cannot be negative: "</span> + age);
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">fullName</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> firstName + <span class="hljs-string">" "</span> + lastName;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isAdult</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> age &gt;= <span class="hljs-number">18</span>;
    }

    <span class="hljs-comment">// Wither method - returns modified copy</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> Person <span class="hljs-title">withAge</span><span class="hljs-params">(<span class="hljs-keyword">int</span> newAge)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Person(firstName, lastName, newAge);
    }
}

<span class="hljs-comment">// Money record with business logic</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Money</span><span class="hljs-params">(BigDecimal amount, String currency)</span> </span>{
    <span class="hljs-keyword">public</span> Money {
        <span class="hljs-keyword">if</span> (amount.compareTo(BigDecimal.ZERO) &lt; <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Amount cannot be negative"</span>);
        }
        currency = currency.toUpperCase();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> Money <span class="hljs-title">add</span><span class="hljs-params">(Money other)</span> </span>{
        <span class="hljs-keyword">if</span> (!currency.equals(other.currency)) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Currency mismatch"</span>);
        }
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Money(amount.add(other.amount), currency);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Money <span class="hljs-title">dollars</span><span class="hljs-params">(<span class="hljs-keyword">double</span> amount)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Money(BigDecimal.valueOf(amount), <span class="hljs-string">"USD"</span>);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RecordMethodsExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-comment">// Person examples</span>
        Person person = <span class="hljs-keyword">new</span> Person(<span class="hljs-string">"  Alice  "</span>, <span class="hljs-string">"  Smith  "</span>, <span class="hljs-number">30</span>);
        System.out.println(<span class="hljs-string">"Person: "</span> + person);
        System.out.println(<span class="hljs-string">"Full name: "</span> + person.fullName());
        System.out.println(<span class="hljs-string">"Is adult: "</span> + person.isAdult());

        <span class="hljs-comment">// Wither pattern</span>
        Person older = person.withAge(<span class="hljs-number">31</span>);
        System.out.println(<span class="hljs-string">"After birthday: "</span> + older);
        System.out.println(<span class="hljs-string">"Original unchanged: "</span> + person);

        <span class="hljs-comment">// Money examples</span>
        Money m1 = Money.dollars(<span class="hljs-number">10.50</span>);
        Money m2 = Money.dollars(<span class="hljs-number">5.25</span>);
        System.out.println(<span class="hljs-string">"\nm1: "</span> + m1);
        System.out.println(<span class="hljs-string">"m2: "</span> + m2);
        System.out.println(<span class="hljs-string">"Sum: "</span> + m1.add(m2));
    }
}
</code></pre>
<pre><code class="lang-java"><span class="hljs-comment">// Unit tests</span>

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should normalize names and validate age in Person")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldNormalizeNamesAndValidateAge</span><span class="hljs-params">()</span> </span>{
    Person person = <span class="hljs-keyword">new</span> Person(<span class="hljs-string">"  Alice  "</span>, <span class="hljs-string">"  Smith  "</span>, <span class="hljs-number">30</span>);

    assertEquals(<span class="hljs-string">"Alice"</span>, person.firstName());
    assertEquals(<span class="hljs-string">"Smith"</span>, person.lastName());
    assertEquals(<span class="hljs-string">"Alice Smith"</span>, person.fullName());
    assertTrue(person.isAdult());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create modified copy using wither pattern")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateModifiedCopyUsingWitherPattern</span><span class="hljs-params">()</span> </span>{
    Person p1 = <span class="hljs-keyword">new</span> Person(<span class="hljs-string">"Bob"</span>, <span class="hljs-string">"Jones"</span>, <span class="hljs-number">15</span>);
    assertFalse(p1.isAdult());

    Person p2 = p1.withAge(<span class="hljs-number">25</span>);

    assertTrue(p2.isAdult());
    assertEquals(<span class="hljs-number">25</span>, p2.age());
    assertEquals(<span class="hljs-number">15</span>, p1.age()); <span class="hljs-comment">// Original unchanged</span>
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should add Money with same currency")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldAddMoneyWithSameCurrency</span><span class="hljs-params">()</span> </span>{
    Money m1 = Money.dollars(<span class="hljs-number">10.50</span>);
    Money m2 = <span class="hljs-keyword">new</span> Money(<span class="hljs-keyword">new</span> BigDecimal(<span class="hljs-string">"5.25"</span>), <span class="hljs-string">"USD"</span>);

    Money sum = m1.add(m2);

    assertEquals(<span class="hljs-keyword">new</span> BigDecimal(<span class="hljs-string">"15.75"</span>), sum.amount());
    assertEquals(<span class="hljs-string">"USD"</span>, sum.currency());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should throw exception when adding Money with different currencies")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldThrowExceptionWhenAddingMoneyWithDifferentCurrencies</span><span class="hljs-params">()</span> </span>{
    Money usd = Money.dollars(<span class="hljs-number">10</span>);
    Money eur = <span class="hljs-keyword">new</span> Money(BigDecimal.TEN, <span class="hljs-string">"EUR"</span>);

    assertThrows(IllegalArgumentException.class, () -&gt; usd.add(eur));
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">Person: Person[firstName=Alice, lastName=Smith, age=30]
Full name: Alice Smith
Is adult: true
After birthday: Person[firstName=Alice, lastName=Smith, age=31]
Original unchanged: Person[firstName=Alice, lastName=Smith, age=30]

m1: Money[amount=10.50, currency=USD]
m2: Money[amount=5.25, currency=USD]
Sum: Money[amount=15.75, currency=USD]
</code></pre>
<p><strong>Key Insight</strong>: The "wither" pattern (<code>withAge()</code>) is common in records - since records are immutable, methods that would "modify" state instead return a new instance with the changed value.</p>
<h3 id="heading-example-4-records-implementing-interfaces">Example 4: Records Implementing Interfaces</h3>
<p>Records work seamlessly with interfaces, enabling polymorphism while maintaining concise syntax.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Interface for drawable objects</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Drawable</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">draw</span><span class="hljs-params">()</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">double</span> <span class="hljs-title">area</span><span class="hljs-params">()</span></span>;

    <span class="hljs-function"><span class="hljs-keyword">default</span> String <span class="hljs-title">description</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Drawable shape with area: "</span> + area();
    }
}

<span class="hljs-comment">// Circle record implementing Drawable</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Circle</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y, <span class="hljs-keyword">int</span> radius)</span> implements Drawable </span>{
    <span class="hljs-keyword">public</span> Circle {
        <span class="hljs-keyword">if</span> (radius &lt;= <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Radius must be positive"</span>);
        }
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">draw</span><span class="hljs-params">()</span> </span>{
        System.out.println(<span class="hljs-string">"Drawing circle at ("</span> + x + <span class="hljs-string">", "</span> + y +
                         <span class="hljs-string">") with radius "</span> + radius);
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> <span class="hljs-title">area</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> Math.PI * radius * radius;
    }
}

<span class="hljs-comment">// Rectangle record implementing Drawable</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Rectangle</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y, <span class="hljs-keyword">int</span> width, <span class="hljs-keyword">int</span> height)</span> implements Drawable </span>{
    <span class="hljs-keyword">public</span> Rectangle {
        <span class="hljs-keyword">if</span> (width &lt;= <span class="hljs-number">0</span> || height &lt;= <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Width and height must be positive"</span>);
        }
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">draw</span><span class="hljs-params">()</span> </span>{
        System.out.println(<span class="hljs-string">"Drawing rectangle at ("</span> + x + <span class="hljs-string">", "</span> + y + <span class="hljs-string">") "</span> +
                         width + <span class="hljs-string">"x"</span> + height);
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">double</span> <span class="hljs-title">area</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> width * height;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isSquare</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> width == height;
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RecordInterfaceExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-comment">// Drawable shapes</span>
        Circle circle = <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>, <span class="hljs-number">5</span>);
        circle.draw();
        System.out.println(<span class="hljs-string">"Area: "</span> + circle.area());
        System.out.println(<span class="hljs-string">"Description: "</span> + circle.description());

        Rectangle rect = <span class="hljs-keyword">new</span> Rectangle(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">100</span>, <span class="hljs-number">50</span>);
        rect.draw();
        System.out.println(<span class="hljs-string">"Area: "</span> + rect.area());
        System.out.println(<span class="hljs-string">"Is square: "</span> + rect.isSquare());

        <span class="hljs-comment">// Polymorphism</span>
        List&lt;Drawable&gt; shapes = List.of(
            <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">10</span>),
            <span class="hljs-keyword">new</span> Rectangle(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">20</span>, <span class="hljs-number">30</span>),
            <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">5</span>, <span class="hljs-number">5</span>, <span class="hljs-number">15</span>)
        );

        System.out.println(<span class="hljs-string">"\nPolymorphic behavior:"</span>);
        <span class="hljs-keyword">for</span> (Drawable shape : shapes) {
            System.out.println(shape.getClass().getSimpleName() + <span class="hljs-string">" - "</span> +
                             shape.description());
        }
    }
}
</code></pre>
<pre><code class="lang-java"><span class="hljs-comment">// Unit tests</span>

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should implement Drawable interface for Circle and Rectangle")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldImplementDrawableInterface</span><span class="hljs-params">()</span> </span>{
    Circle circle = <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>, <span class="hljs-number">5</span>);
    Rectangle rect = <span class="hljs-keyword">new</span> Rectangle(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">100</span>, <span class="hljs-number">50</span>);

    assertTrue(circle <span class="hljs-keyword">instanceof</span> Drawable);
    assertTrue(rect <span class="hljs-keyword">instanceof</span> Drawable);
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should calculate areas correctly")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCalculateAreasCorrectly</span><span class="hljs-params">()</span> </span>{
    Circle c = <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">5</span>);
    Rectangle r = <span class="hljs-keyword">new</span> Rectangle(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">10</span>, <span class="hljs-number">20</span>);

    assertTrue(c.area() &gt; <span class="hljs-number">78</span> &amp;&amp; c.area() &lt; <span class="hljs-number">79</span>); <span class="hljs-comment">// π * 5²</span>
    assertEquals(<span class="hljs-number">200.0</span>, r.area(), <span class="hljs-number">0.001</span>);       <span class="hljs-comment">// 10 * 20</span>
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should support polymorphic collections")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldSupportPolymorphicCollections</span><span class="hljs-params">()</span> </span>{
    List&lt;Drawable&gt; shapes = List.of(
        <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>, <span class="hljs-number">5</span>),
        <span class="hljs-keyword">new</span> Rectangle(<span class="hljs-number">30</span>, <span class="hljs-number">40</span>, <span class="hljs-number">100</span>, <span class="hljs-number">50</span>),
        <span class="hljs-keyword">new</span> Circle(<span class="hljs-number">60</span>, <span class="hljs-number">70</span>, <span class="hljs-number">15</span>)
    );

    assertEquals(<span class="hljs-number">3</span>, shapes.size());
    assertTrue(shapes.get(<span class="hljs-number">0</span>) <span class="hljs-keyword">instanceof</span> Circle);
    assertTrue(shapes.get(<span class="hljs-number">1</span>) <span class="hljs-keyword">instanceof</span> Rectangle);
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should check if Rectangle is square")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCheckIfRectangleIsSquare</span><span class="hljs-params">()</span> </span>{
    Rectangle square = <span class="hljs-keyword">new</span> Rectangle(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">10</span>, <span class="hljs-number">10</span>);
    Rectangle rect = <span class="hljs-keyword">new</span> Rectangle(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">10</span>, <span class="hljs-number">20</span>);

    assertTrue(square.isSquare());
    assertFalse(rect.isSquare());
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">Drawing circle at (10, 20) with radius 5
Area: 78.53981633974483
Description: Drawable shape with area: 78.53981633974483
Drawing rectangle at (0, 0) 100x50
Area: 5000.0
Is square: false

Polymorphic behavior:
Circle - Drawable shape with area: 314.1592653589793
Rectangle - Drawable shape with area: 600.0
Circle - Drawable shape with area: 706.8583470577034
</code></pre>
<p><strong>Key Insight</strong>: Records work seamlessly with interfaces, enabling polymorphism while maintaining all the benefits of concise syntax and generated methods.</p>
<h3 id="heading-example-5-nested-records">Example 5: Nested Records</h3>
<p>Records compose naturally to model complex domain objects. Each record maintains its own invariants while composing into larger structures.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Address record - represents a physical address</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Address</span><span class="hljs-params">(String street, String city, String zipCode, String country)</span> </span>{
    <span class="hljs-keyword">public</span> Address {
        <span class="hljs-keyword">if</span> (street == <span class="hljs-keyword">null</span> || street.isBlank()) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Street cannot be blank"</span>);
        }
        <span class="hljs-keyword">if</span> (city == <span class="hljs-keyword">null</span> || city.isBlank()) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"City cannot be blank"</span>);
        }
        <span class="hljs-comment">// Normalize zip code (remove spaces)</span>
        zipCode = zipCode.replaceAll(<span class="hljs-string">"\\s"</span>, <span class="hljs-string">""</span>);
        <span class="hljs-comment">// Normalize country to uppercase</span>
        country = country.toUpperCase();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">format</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> street + <span class="hljs-string">", "</span> + city + <span class="hljs-string">" "</span> + zipCode + <span class="hljs-string">", "</span> + country;
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isInCountry</span><span class="hljs-params">(String countryCode)</span> </span>{
        <span class="hljs-keyword">return</span> country.equalsIgnoreCase(countryCode);
    }
}

<span class="hljs-comment">// Employee record - contains nested Address</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Employee</span><span class="hljs-params">(String name, <span class="hljs-keyword">int</span> id, Address address)</span> </span>{
    <span class="hljs-keyword">public</span> Employee {
        <span class="hljs-keyword">if</span> (name == <span class="hljs-keyword">null</span> || name.isBlank()) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Name cannot be blank"</span>);
        }
        <span class="hljs-keyword">if</span> (id &lt;= <span class="hljs-number">0</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"ID must be positive: "</span> + id);
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getFullAddress</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> address.format();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getCity</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> address.city();
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> Employee <span class="hljs-title">relocate</span><span class="hljs-params">(Address newAddress)</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Employee(name, id, newAddress);
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">worksInCountry</span><span class="hljs-params">(String countryCode)</span> </span>{
        <span class="hljs-keyword">return</span> address.isInCountry(countryCode);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NestedRecordsExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-comment">// Simple nesting</span>
        Address addr = <span class="hljs-keyword">new</span> Address(<span class="hljs-string">"123 Main St"</span>, <span class="hljs-string">"Springfield"</span>, <span class="hljs-string">"12345"</span>, <span class="hljs-string">"USA"</span>);
        Employee emp = <span class="hljs-keyword">new</span> Employee(<span class="hljs-string">"Alice Smith"</span>, <span class="hljs-number">1001</span>, addr);

        System.out.println(<span class="hljs-string">"Employee: "</span> + emp.name());
        System.out.println(<span class="hljs-string">"ID: "</span> + emp.id());
        System.out.println(<span class="hljs-string">"City: "</span> + emp.getCity());
        System.out.println(<span class="hljs-string">"Full address: "</span> + emp.getFullAddress());
        System.out.println(<span class="hljs-string">"Works in USA: "</span> + emp.worksInCountry(<span class="hljs-string">"USA"</span>));

        <span class="hljs-comment">// Relocation</span>
        Address newAddr = <span class="hljs-keyword">new</span> Address(<span class="hljs-string">"456 Oak Ave"</span>, <span class="hljs-string">"Portland"</span>, <span class="hljs-string">"97201"</span>, <span class="hljs-string">"USA"</span>);
        Employee relocated = emp.relocate(newAddr);

        System.out.println(<span class="hljs-string">"\nOriginal city: "</span> + emp.getCity());
        System.out.println(<span class="hljs-string">"New city: "</span> + relocated.getCity());
    }
}
</code></pre>
<pre><code class="lang-java"><span class="hljs-comment">// Unit tests</span>

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should access nested Address properties from Employee")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldAccessNestedAddressProperties</span><span class="hljs-params">()</span> </span>{
    Address addr = <span class="hljs-keyword">new</span> Address(<span class="hljs-string">"123 Main St"</span>, <span class="hljs-string">"Springfield"</span>, <span class="hljs-string">"12345"</span>, <span class="hljs-string">"USA"</span>);
    Employee emp = <span class="hljs-keyword">new</span> Employee(<span class="hljs-string">"Alice Smith"</span>, <span class="hljs-number">1001</span>, addr);

    assertEquals(<span class="hljs-string">"Alice Smith"</span>, emp.name());
    assertEquals(<span class="hljs-string">"Springfield"</span>, emp.address().city());
    assertEquals(<span class="hljs-string">"12345"</span>, emp.address().zipCode());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should format full address from nested record")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldFormatFullAddressFromNestedRecord</span><span class="hljs-params">()</span> </span>{
    Address addr = <span class="hljs-keyword">new</span> Address(<span class="hljs-string">"123 Main St"</span>, <span class="hljs-string">"Springfield"</span>, <span class="hljs-string">"12345"</span>, <span class="hljs-string">"USA"</span>);
    Employee emp = <span class="hljs-keyword">new</span> Employee(<span class="hljs-string">"Bob Jones"</span>, <span class="hljs-number">1002</span>, addr);

    String expected = <span class="hljs-string">"123 Main St, Springfield 12345, USA"</span>;
    assertEquals(expected, emp.getFullAddress());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should create relocated Employee with new Address")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldCreateRelocatedEmployeeWithNewAddress</span><span class="hljs-params">()</span> </span>{
    Address addr1 = <span class="hljs-keyword">new</span> Address(<span class="hljs-string">"123 Main St"</span>, <span class="hljs-string">"Springfield"</span>, <span class="hljs-string">"12345"</span>, <span class="hljs-string">"USA"</span>);
    Employee emp1 = <span class="hljs-keyword">new</span> Employee(<span class="hljs-string">"Charlie"</span>, <span class="hljs-number">1003</span>, addr1);

    Address addr2 = <span class="hljs-keyword">new</span> Address(<span class="hljs-string">"456 Oak Ave"</span>, <span class="hljs-string">"Portland"</span>, <span class="hljs-string">"97201"</span>, <span class="hljs-string">"USA"</span>);
    Employee emp2 = emp1.relocate(addr2);

    assertEquals(<span class="hljs-string">"Portland"</span>, emp2.address().city());
    assertEquals(<span class="hljs-string">"Springfield"</span>, emp1.address().city()); <span class="hljs-comment">// Original unchanged</span>
    assertEquals(emp1.id(), emp2.id());
    assertEquals(emp1.name(), emp2.name());
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should normalize zip code removing spaces")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldNormalizeZipCodeRemovingSpaces</span><span class="hljs-params">()</span> </span>{
    Address addr = <span class="hljs-keyword">new</span> Address(<span class="hljs-string">"123 Main"</span>, <span class="hljs-string">"City"</span>, <span class="hljs-string">"12 345"</span>, <span class="hljs-string">"USA"</span>);
    assertEquals(<span class="hljs-string">"12345"</span>, addr.zipCode());
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">Employee: Alice Smith
ID: 1001
City: Springfield
Full address: 123 Main St, Springfield 12345, USA
Works in USA: true

Original city: Springfield
New city: Portland
</code></pre>
<p><strong>Key Insight</strong>: Nested records model complex domain objects naturally. Each record maintains its own invariants while composing into larger structures.</p>
<h3 id="heading-example-6-performance-considerations">Example 6: Performance Considerations</h3>
<p>Understanding how records perform is crucial for production use - memory layout, equality operations, and collection performance.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Small record for memory demonstration</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">SmallRecord</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{
}

<span class="hljs-comment">// Larger record for comparison</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">LargeRecord</span><span class="hljs-params">(<span class="hljs-keyword">long</span> a, <span class="hljs-keyword">long</span> b, <span class="hljs-keyword">long</span> c, <span class="hljs-keyword">long</span> d, <span class="hljs-keyword">long</span> e)</span> </span>{
}

<span class="hljs-comment">// Record for equality testing</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">TestRecord</span><span class="hljs-params">(String field1, String field2, <span class="hljs-keyword">int</span> field3, <span class="hljs-keyword">long</span> field4)</span> </span>{
}

<span class="hljs-comment">// Coordinate record for collection performance</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Coordinate</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">manhattanDistance</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> Math.abs(x) + Math.abs(y);
    }
}

<span class="hljs-comment">// Color record demonstrating caching pattern</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Color</span><span class="hljs-params">(<span class="hljs-keyword">int</span> r, <span class="hljs-keyword">int</span> g, <span class="hljs-keyword">int</span> b)</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Map&lt;String, Color&gt; CACHE = <span class="hljs-keyword">new</span> ConcurrentHashMap&lt;&gt;();

    <span class="hljs-keyword">public</span> Color {
        <span class="hljs-keyword">if</span> (r &lt; <span class="hljs-number">0</span> || r &gt; <span class="hljs-number">255</span> || g &lt; <span class="hljs-number">0</span> || g &gt; <span class="hljs-number">255</span> || b &lt; <span class="hljs-number">0</span> || b &gt; <span class="hljs-number">255</span>) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(
                <span class="hljs-string">"RGB values must be 0-255: r="</span> + r + <span class="hljs-string">", g="</span> + g + <span class="hljs-string">", b="</span> + b
            );
        }
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> Color <span class="hljs-title">of</span><span class="hljs-params">(<span class="hljs-keyword">int</span> r, <span class="hljs-keyword">int</span> g, <span class="hljs-keyword">int</span> b)</span> </span>{
        String key = r + <span class="hljs-string">","</span> + g + <span class="hljs-string">","</span> + b;
        <span class="hljs-keyword">return</span> CACHE.computeIfAbsent(key, k -&gt; <span class="hljs-keyword">new</span> Color(r, g, b));
    }

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">clearCache</span><span class="hljs-params">()</span> </span>{
        CACHE.clear();
    }

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Color RED = Color.of(<span class="hljs-number">255</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Color GREEN = Color.of(<span class="hljs-number">0</span>, <span class="hljs-number">255</span>, <span class="hljs-number">0</span>);
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Color BLUE = Color.of(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">255</span>);

    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toHex</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> String.format(<span class="hljs-string">"#%02X%02X%02X"</span>, r, g, b);
    }
}

<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RecordPerformanceExample</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
        <span class="hljs-comment">// Memory estimates</span>
        System.out.println(<span class="hljs-string">"=== Memory Layout ==="</span>);
        System.out.println(<span class="hljs-string">"SmallRecord: ~24 bytes"</span>);
        System.out.println(<span class="hljs-string">"LargeRecord: ~56 bytes"</span>);

        <span class="hljs-comment">// Equality performance</span>
        System.out.println(<span class="hljs-string">"\n=== Equality Performance ==="</span>);
        TestRecord r1 = <span class="hljs-keyword">new</span> TestRecord(<span class="hljs-string">"alpha"</span>, <span class="hljs-string">"beta"</span>, <span class="hljs-number">100</span>, <span class="hljs-number">200L</span>);
        TestRecord r2 = <span class="hljs-keyword">new</span> TestRecord(<span class="hljs-string">"alpha"</span>, <span class="hljs-string">"beta"</span>, <span class="hljs-number">100</span>, <span class="hljs-number">200L</span>);

        <span class="hljs-keyword">int</span> iterations = <span class="hljs-number">1_000_000</span>;
        <span class="hljs-keyword">long</span> start = System.nanoTime();
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; iterations; i++) {
            r1.equals(r2);
        }
        <span class="hljs-keyword">long</span> duration = System.nanoTime() - start;
        System.out.println(<span class="hljs-string">"1M equality checks: "</span> + duration / <span class="hljs-number">1_000_000</span> + <span class="hljs-string">"ms"</span>);

        <span class="hljs-comment">// HashMap performance</span>
        System.out.println(<span class="hljs-string">"\n=== HashMap Performance ==="</span>);
        Map&lt;Coordinate, String&gt; map = <span class="hljs-keyword">new</span> HashMap&lt;&gt;();
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">1000</span>; i++) {
            map.put(<span class="hljs-keyword">new</span> Coordinate(i, i * <span class="hljs-number">2</span>), <span class="hljs-string">"value"</span> + i);
        }
        System.out.println(<span class="hljs-string">"Inserted 1000 records as keys"</span>);
        System.out.println(<span class="hljs-string">"Lookup test: "</span> + map.get(<span class="hljs-keyword">new</span> Coordinate(<span class="hljs-number">500</span>, <span class="hljs-number">1000</span>)));

        <span class="hljs-comment">// Caching pattern</span>
        System.out.println(<span class="hljs-string">"\n=== Caching Pattern ==="</span>);
        Color.clearCache();
        Color red1 = Color.of(<span class="hljs-number">255</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
        Color red2 = Color.of(<span class="hljs-number">255</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
        System.out.println(<span class="hljs-string">"red1 == red2 (same instance): "</span> + (red1 == red2));
        System.out.println(<span class="hljs-string">"RED constant: "</span> + Color.RED.toHex());
    }
}
</code></pre>
<pre><code class="lang-java"><span class="hljs-comment">// Unit tests</span>

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should have comparable memory overhead to manual classes")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldHaveComparableMemoryOverhead</span><span class="hljs-params">()</span> </span>{
    SmallRecord small = <span class="hljs-keyword">new</span> SmallRecord(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>);
    LargeRecord large = <span class="hljs-keyword">new</span> LargeRecord(<span class="hljs-number">1L</span>, <span class="hljs-number">2L</span>, <span class="hljs-number">3L</span>, <span class="hljs-number">4L</span>, <span class="hljs-number">5L</span>);

    <span class="hljs-comment">// Records have same overhead as equivalent manual classes</span>
    assertNotNull(small);
    assertNotNull(large);
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should perform equality checks efficiently")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldPerformEqualityChecksEfficiently</span><span class="hljs-params">()</span> </span>{
    TestRecord r1 = <span class="hljs-keyword">new</span> TestRecord(<span class="hljs-string">"alpha"</span>, <span class="hljs-string">"beta"</span>, <span class="hljs-number">100</span>, <span class="hljs-number">200L</span>);
    TestRecord r2 = <span class="hljs-keyword">new</span> TestRecord(<span class="hljs-string">"alpha"</span>, <span class="hljs-string">"beta"</span>, <span class="hljs-number">100</span>, <span class="hljs-number">200L</span>);

    <span class="hljs-keyword">long</span> start = System.nanoTime();
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">1_000_000</span>; i++) {
        r1.equals(r2);
    }
    <span class="hljs-keyword">long</span> duration = System.nanoTime() - start;

    assertTrue(duration &lt; <span class="hljs-number">100_000_000</span>); <span class="hljs-comment">// Under 100ms</span>
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should work efficiently as HashMap keys")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldWorkEfficientlyAsHashMapKeys</span><span class="hljs-params">()</span> </span>{
    Map&lt;Coordinate, String&gt; map = <span class="hljs-keyword">new</span> HashMap&lt;&gt;();

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">1000</span>; i++) {
        map.put(<span class="hljs-keyword">new</span> Coordinate(i, i * <span class="hljs-number">2</span>), <span class="hljs-string">"value"</span> + i);
    }

    Coordinate key = <span class="hljs-keyword">new</span> Coordinate(<span class="hljs-number">500</span>, <span class="hljs-number">1000</span>);
    assertEquals(<span class="hljs-string">"value500"</span>, map.get(key));

    <span class="hljs-comment">// Check hash distribution</span>
    Set&lt;Integer&gt; hashCodes = <span class="hljs-keyword">new</span> HashSet&lt;&gt;();
    <span class="hljs-keyword">for</span> (Coordinate coord : map.keySet()) {
        hashCodes.add(coord.hashCode());
    }
    assertTrue(hashCodes.size() &gt; <span class="hljs-number">950</span>); <span class="hljs-comment">// Good hash distribution</span>
}

<span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should support caching pattern with factory method")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldSupportCachingPatternWithFactoryMethod</span><span class="hljs-params">()</span> </span>{
    Color.clearCache();
    Color red1 = Color.of(<span class="hljs-number">255</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
    Color red2 = Color.of(<span class="hljs-number">255</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

    assertSame(red1, red2); <span class="hljs-comment">// Same instance from cache</span>
    assertEquals(red1, red2); <span class="hljs-comment">// Also value equality</span>
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">=== Memory Layout ===
SmallRecord: ~24 bytes
LargeRecord: ~56 bytes

=== Equality Performance ===
1M equality checks: 15ms

=== HashMap Performance ===
Inserted 1000 records as keys
Lookup test: value500

=== Caching Pattern ===
red1 == red2 (same instance): true
RED constant: #FF0000
</code></pre>
<p><strong>Key Insights:</strong></p>
<ul>
<li><p>Records have the same memory overhead as equivalent manual classes</p>
</li>
<li><p>Object header: 12-16 bytes (depends on JVM settings)</p>
</li>
<li><p>Components stored as instance fields (primitives inline, references as pointers)</p>
</li>
<li><p>No additional overhead for generated methods</p>
</li>
<li><p>Equality performance depends on number and types of components</p>
</li>
<li><p>Records work excellently as map keys due to consistent <code>equals()</code> and <code>hashCode()</code></p>
</li>
<li><p>Caching pattern reduces memory footprint for frequently used records</p>
</li>
</ul>
<hr />
<h2 id="heading-pitfalls-and-limitations">Pitfalls and Limitations</h2>
<p>Records have intentional restrictions that prevent common mistakes but can surprise developers coming from traditional classes.</p>
<h3 id="heading-mutable-components">Mutable Components</h3>
<p>Records are only as immutable as their components. Arrays and collections are mutable:</p>
<pre><code class="lang-java"><span class="hljs-comment">// ❌ PROBLEM: Array component can be modified</span>
<span class="hljs-function">record <span class="hljs-title">BadRecord</span><span class="hljs-params">(<span class="hljs-keyword">int</span>[] data)</span> </span>{
}

<span class="hljs-comment">// Usage</span>
<span class="hljs-keyword">int</span>[] array = {<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>};
BadRecord record = <span class="hljs-keyword">new</span> BadRecord(array);
array[<span class="hljs-number">0</span>] = <span class="hljs-number">999</span>; <span class="hljs-comment">// Modifies the record's internal state!</span>

<span class="hljs-comment">// ✅ SOLUTION: Defensive copying</span>
<span class="hljs-function">record <span class="hljs-title">GoodRecord</span><span class="hljs-params">(<span class="hljs-keyword">int</span>[] data)</span> </span>{
    <span class="hljs-keyword">public</span> GoodRecord {
        data = data.clone(); <span class="hljs-comment">// Copy on construction</span>
    }

    <span class="hljs-meta">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span>[] data() {
        <span class="hljs-keyword">return</span> data.clone(); <span class="hljs-comment">// Copy on access</span>
    }
}
</code></pre>
<h3 id="heading-collections-require-special-handling">Collections Require Special Handling</h3>
<p>Collections need defensive copying and immutability:</p>
<pre><code class="lang-java"><span class="hljs-comment">// ❌ PROBLEM: Mutable list</span>
<span class="hljs-function">record <span class="hljs-title">BadTeam</span><span class="hljs-params">(String name, List&lt;String&gt; members)</span> </span>{
}

List&lt;String&gt; members = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();
members.add(<span class="hljs-string">"Alice"</span>);
BadTeam team = <span class="hljs-keyword">new</span> BadTeam(<span class="hljs-string">"Dev Team"</span>, members);
members.add(<span class="hljs-string">"Bob"</span>); <span class="hljs-comment">// Modifies team's internal list!</span>

<span class="hljs-comment">// ✅ SOLUTION: Use immutable copy</span>
<span class="hljs-function">record <span class="hljs-title">GoodTeam</span><span class="hljs-params">(String name, List&lt;String&gt; members)</span> </span>{
    <span class="hljs-keyword">public</span> GoodTeam {
        members = List.copyOf(members); <span class="hljs-comment">// Immutable defensive copy</span>
    }
}
</code></pre>
<h3 id="heading-cannot-extend-classes">Cannot Extend Classes</h3>
<p>Records implicitly extend <code>java.lang.Record</code> and cannot extend other classes:</p>
<pre><code class="lang-java"><span class="hljs-comment">// ❌ Won't compile</span>
<span class="hljs-function">record <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> extends SomeClass </span>{
}

<span class="hljs-comment">// ✅ Can implement interfaces</span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Positioned</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">x</span><span class="hljs-params">()</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">y</span><span class="hljs-params">()</span></span>;
}

<span class="hljs-function">record <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> implements Positioned </span>{
}
</code></pre>
<h3 id="heading-final-by-nature">Final by Nature</h3>
<p>Records are implicitly final - they cannot be subclassed:</p>
<pre><code class="lang-java"><span class="hljs-function">record <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{
}

<span class="hljs-comment">// ❌ Won't compile</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Point3D</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Point</span> </span>{
}
</code></pre>
<h3 id="heading-instance-fields-forbidden">Instance Fields Forbidden</h3>
<p>You cannot declare instance fields beyond the components:</p>
<pre><code class="lang-java"><span class="hljs-comment">// ❌ Won't compile</span>
<span class="hljs-function">record <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> z; <span class="hljs-comment">// Compilation error!</span>
}

<span class="hljs-comment">// ✅ Use components or computed values</span>
<span class="hljs-function">record <span class="hljs-title">Point</span><span class="hljs-params">(<span class="hljs-keyword">int</span> x, <span class="hljs-keyword">int</span> y)</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">manhattanDistance</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> Math.abs(x) + Math.abs(y);
    }
}
</code></pre>
<hr />
<h2 id="heading-best-practices">Best Practices</h2>
<h3 id="heading-when-to-use-records">When to Use Records</h3>
<p>✅ <strong>Use records for:</strong></p>
<ul>
<li><p><strong>Data Transfer Objects (DTOs)</strong> - perfect for API request/response models</p>
</li>
<li><p><strong>Value objects</strong> - coordinates, money, dates, addresses</p>
</li>
<li><p><strong>Configuration objects</strong> - immutable settings</p>
</li>
<li><p><strong>Grouping data in streams</strong> - temporary structures in processing pipelines</p>
</li>
<li><p><strong>Domain value types</strong> - anything representing a "value" rather than an "entity"</p>
</li>
<li><p><strong>Return values</strong> - replacing arrays or lists for multiple return values</p>
</li>
</ul>
<pre><code class="lang-java"><span class="hljs-comment">// Excellent record use case</span>
<span class="hljs-function">record <span class="hljs-title">ApiResponse</span><span class="hljs-params">(<span class="hljs-keyword">int</span> statusCode, String message, LocalDateTime timestamp)</span> </span>{
}

<span class="hljs-function">record <span class="hljs-title">Coordinates</span><span class="hljs-params">(<span class="hljs-keyword">double</span> latitude, <span class="hljs-keyword">double</span> longitude)</span> </span>{
}

<span class="hljs-function">record <span class="hljs-title">Money</span><span class="hljs-params">(BigDecimal amount, Currency currency)</span> </span>{
}
</code></pre>
<h3 id="heading-when-not-to-use-records">When NOT to Use Records</h3>
<p>❌ <strong>Don't use records when:</strong></p>
<ul>
<li><p><strong>You need mutable state</strong> - records are for immutable data</p>
</li>
<li><p><strong>You need inheritance</strong> - records can't extend classes</p>
</li>
<li><p><strong>You want to hide representation</strong> - records are transparent by design</p>
</li>
<li><p><strong>You need JavaBeans conventions</strong> - records use <code>field()</code> not <code>getField()</code></p>
</li>
<li><p><strong>Complex lazy initialization needed</strong> - all fields must be set in constructor</p>
</li>
</ul>
<pre><code class="lang-java"><span class="hljs-comment">// Bad record use - this should be a class</span>
<span class="hljs-function">record <span class="hljs-title">UserSession</span><span class="hljs-params">(String userId, List&lt;Action&gt; actions)</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">addAction</span><span class="hljs-params">(Action a)</span> </span>{ <span class="hljs-comment">// ❌ Violates immutability</span>
        actions.add(a);
    }
}

<span class="hljs-comment">// Good alternative - use a class with encapsulation</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserSession</span> </span>{
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> String userId;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> List&lt;Action&gt; actions = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();

    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">addAction</span><span class="hljs-params">(Action a)</span> </span>{
        actions.add(a);
    }
}
</code></pre>
<h3 id="heading-immutable-collections-pattern">Immutable Collections Pattern</h3>
<p>Always use <code>List.copyOf()</code> for collection components:</p>
<pre><code class="lang-java"><span class="hljs-function">record <span class="hljs-title">Team</span><span class="hljs-params">(String name, List&lt;String&gt; members)</span> </span>{
    <span class="hljs-keyword">public</span> Team {
        members = List.copyOf(members); <span class="hljs-comment">// Immutable copy</span>
    }
}

<span class="hljs-comment">// Usage</span>
List&lt;String&gt; mutableList = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();
mutableList.add(<span class="hljs-string">"Alice"</span>);
Team team = <span class="hljs-keyword">new</span> Team(<span class="hljs-string">"Dev"</span>, mutableList);

mutableList.add(<span class="hljs-string">"Bob"</span>); <span class="hljs-comment">// Doesn't affect team</span>
assertEquals(<span class="hljs-number">1</span>, team.members().size());
</code></pre>
<h3 id="heading-validation-in-compact-constructor">Validation in Compact Constructor</h3>
<p>Put all validation in the compact constructor:</p>
<pre><code class="lang-java"><span class="hljs-function">record <span class="hljs-title">Email</span><span class="hljs-params">(String address)</span> </span>{
    <span class="hljs-keyword">public</span> Email {
        <span class="hljs-keyword">if</span> (address == <span class="hljs-keyword">null</span> || !address.contains(<span class="hljs-string">"@"</span>)) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Invalid email"</span>);
        }
        address = address.toLowerCase(); <span class="hljs-comment">// Normalize</span>
    }
}
</code></pre>
<h3 id="heading-document-invariants">Document Invariants</h3>
<p>Use JavaDoc to document invariants enforced by validation:</p>
<pre><code class="lang-java"><span class="hljs-comment">/**
 * Represents a valid email address.
 *
 * &lt;p&gt;Invariants:
 * &lt;ul&gt;
 *   &lt;li&gt;Address is non-null&lt;/li&gt;
 *   &lt;li&gt;Address contains '@' character&lt;/li&gt;
 *   &lt;li&gt;Address is normalized to lowercase&lt;/li&gt;
 * &lt;/ul&gt;
 */</span>
<span class="hljs-function">record <span class="hljs-title">Email</span><span class="hljs-params">(String address)</span> </span>{
    <span class="hljs-keyword">public</span> Email {
        <span class="hljs-keyword">if</span> (address == <span class="hljs-keyword">null</span> || !address.contains(<span class="hljs-string">"@"</span>)) {
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Invalid email"</span>);
        }
        address = address.toLowerCase();
    }
}
</code></pre>
<hr />
<h2 id="heading-summary-and-next-steps">Summary and Next Steps</h2>
<h3 id="heading-key-takeaways">Key Takeaways</h3>
<ol>
<li><p><strong>Records eliminate boilerplate</strong> - one line replaces 30+ lines of code</p>
</li>
<li><p><strong>Immutability by default</strong> - all components are final</p>
</li>
<li><p><strong>Value semantics</strong> - structural equality, perfect for DTOs and value objects</p>
</li>
<li><p><strong>Compact constructor</strong> - concise validation and normalization</p>
</li>
<li><p><strong>Not a silver bullet</strong> - use for data carriers, not entities with behavior</p>
</li>
<li><p><strong>Defensive copying required</strong> - for arrays and collections</p>
</li>
<li><p><strong>Performance comparable</strong> - to equivalent manual classes</p>
</li>
</ol>
<h3 id="heading-records-vs-alternatives">Records vs Alternatives</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Feature</td><td>Records</td><td>Lombok @Value</td><td>Manual Class</td></tr>
</thead>
<tbody>
<tr>
<td>Language support</td><td>Native</td><td>Library dependency</td><td>Native</td></tr>
<tr>
<td>IDE support</td><td>Built-in</td><td>Requires plugin</td><td>Built-in</td></tr>
<tr>
<td>Boilerplate</td><td>Minimal</td><td>Minimal</td><td>High</td></tr>
<tr>
<td>Flexibility</td><td>Restricted</td><td>High</td><td>Unlimited</td></tr>
<tr>
<td>Immutability</td><td>Enforced</td><td>Optional</td><td>Manual</td></tr>
<tr>
<td>Pattern matching</td><td>Full support</td><td>None</td><td>Limited</td></tr>
</tbody>
</table>
</div><h3 id="heading-coming-up-next">Coming Up Next</h3>
<p><strong>Part 3: Sealed Classes (JEP 409)</strong> - Controlling type hierarchies</p>
<p>Sealed classes let you restrict which classes can extend or implement a type, enabling:</p>
<ul>
<li><p>Exhaustive pattern matching without default cases</p>
</li>
<li><p>Closed type hierarchies for domain modeling</p>
</li>
<li><p>Compiler-verified completeness in switch statements</p>
</li>
<li><p>Better API design with controlled inheritance</p>
</li>
</ul>
<p>We'll explore how sealed classes combine with records to create powerful, type-safe domain models.</p>
<h3 id="heading-resources">Resources</h3>
<h4 id="heading-official-documentation">Official Documentation</h4>
<ul>
<li><p><strong>JEP 395 - Records</strong>: <a target="_blank" href="https://openjdk.org/jeps/395">openjdk.org/jeps/395</a> The official JEP that made records a standard feature in Java 16. Contains design rationale, grammar specification, and migration guide.</p>
</li>
<li><p><strong>Java Language Specification - Records</strong>: <a target="_blank" href="https://docs.oracle.com/javase/specs/jls/se17/html/jls-8.html#jls-8.10">docs.oracle.com/javase/specs/jls/se17/html/jls-8.html#jls-8.10</a> Formal language specification for record classes.</p>
</li>
</ul>
<h4 id="heading-interactive-references">Interactive References</h4>
<ul>
<li><strong>Java Almanac - Records</strong>: <a target="_blank" href="https://javaalmanac.io/features/records/">javaalmanac.io/features/records/</a> Interactive examples and timeline of record features across Java versions.</li>
</ul>
<h4 id="heading-code-repository">Code Repository</h4>
<ul>
<li><p><strong>GitHub Repository</strong>: <a target="_blank" href="https://github.com/dawid-swist/blog-9mac-dev-code">github.com/dawid-swist/blog-9mac-dev-code</a> All examples from this article with full tests:</p>
<pre><code class="lang-bash">  git <span class="hljs-built_in">clone</span> https://github.com/dawid-swist/blog-9mac-dev-code.git
  <span class="hljs-built_in">cd</span> blog-post-examples/java/2025-10-25-java17-features-every-senior-developer-should-know
  ../../gradlew <span class="hljs-built_in">test</span>
</code></pre>
</li>
</ul>
<hr />
<h2 id="heading-run-the-examples">Run the Examples</h2>
<p>All code examples are available in the repository:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Run individual tests</span>
./gradlew <span class="hljs-built_in">test</span> --tests BasicRecordExampleTest
./gradlew <span class="hljs-built_in">test</span> --tests GenericRecordExampleTest
./gradlew <span class="hljs-built_in">test</span> --tests RecordMethodsExampleTest
./gradlew <span class="hljs-built_in">test</span> --tests RecordInterfaceExampleTest
./gradlew <span class="hljs-built_in">test</span> --tests NestedRecordsExampleTest
./gradlew <span class="hljs-built_in">test</span> --tests RecordPerformanceExampleTest

<span class="hljs-comment"># Run all Part 2 tests</span>
./gradlew <span class="hljs-built_in">test</span> --tests *part2*
</code></pre>
<hr />
<p><em>Written for</em> <a target="_blank" href="https://blog.9mac.dev"><em>blog.9mac.dev</em></a> <em>Part of the "Java 17 Features Every Senior Developer Should Know" series</em></p>
<p><strong>Previous</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-1-introduction-and-var-keyword">Part 1 - Introduction &amp; var Keyword</a> <strong>Next</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-3-sealed-classes">Part 3 - Sealed Classes</a></p>
]]></content:encoded></item><item><title><![CDATA[Java 17 Features Every Senior Developer Should Know - Part 1: Introduction & var Keyword]]></title><description><![CDATA[Welcome to the first installment of our comprehensive six-part series on Java 17 features! Whether you're a senior developer whose Java knowledge plateaued at version 8, a junior developer learning modern Java from scratch, or part of a team planning...]]></description><link>https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-1-introduction-and-var-keyword</link><guid isPermaLink="true">https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-1-introduction-and-var-keyword</guid><category><![CDATA[Java]]></category><category><![CDATA[java 17]]></category><category><![CDATA[Java 17 Features Every Senior Developer Should Know]]></category><dc:creator><![CDATA[Dawid Świst]]></dc:creator><pubDate>Tue, 28 Oct 2025 23:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761766999317/d1b72eda-2814-4677-99bd-54fba91c91a9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome to the first installment of our comprehensive six-part series on Java 17 features! Whether you're a senior developer whose Java knowledge plateaued at version 8, a junior developer learning modern Java from scratch, or part of a team planning a migration to Java 17 LTS, this series is designed for you.</p>
<hr />
<h2 id="heading-welcome-to-the-series">Welcome to the Series</h2>
<p>Welcome to part one of "Java 17 Features Every Senior Developer Should Know"! Whether your Java knowledge stopped at version 8 or you're just starting your journey with the language, this series is for you. Over six articles, we'll explore the most important features introduced in Java 10-17, combining theory with practical examples and hands-on exercises.</p>
<p><strong>What you'll learn in this series:</strong></p>
<ul>
<li><p><strong>var keyword (Java 10)</strong> - Local variable type inference that eliminates repetitive type declarations</p>
</li>
<li><p><strong>Records (Java 16)</strong> - Immutable data carriers with zero boilerplate for DTOs and value objects</p>
</li>
<li><p><strong>Sealed Classes (Java 17)</strong> - Fine-grained control over class hierarchies and permitted subtypes</p>
</li>
<li><p><strong>Pattern Matching (Java 16)</strong> - Smarter <code>instanceof</code> checks that eliminate redundant casts</p>
</li>
<li><p><strong>Switch Expressions (Java 14)</strong> - Modern switch syntax with arrow notation and return values</p>
</li>
<li><p><strong>Text Blocks (Java 15)</strong> - Multi-line string literals without escape character chaos</p>
</li>
</ul>
<p>Each part includes runnable examples with JUnit tests. All code is in the GitHub repository—clone it and start experimenting right away!</p>
<h3 id="heading-why-java-17">Why Java 17?</h3>
<p>Released in September 2021, Java 17 is the third LTS (Long-Term Support) release after Java 8 and 11. It's not just another version—it's the culmination of seven years of language evolution, bringing together features that were previewed and refined across Java 12-16.</p>
<p><strong>Key features by version:</strong></p>
<ul>
<li><p><strong>Java 10 (JEP 286)</strong> - <code>var</code> keyword for local variable type inference</p>
</li>
<li><p><strong>Java 14 (JEP 361)</strong> - Switch expressions with arrow syntax and <code>yield</code></p>
</li>
<li><p><strong>Java 15 (JEP 378)</strong> - Text blocks for readable multi-line strings</p>
</li>
<li><p><strong>Java 16 (JEP 394)</strong> - Pattern matching for <code>instanceof</code></p>
</li>
<li><p><strong>Java 16 (JEP 395)</strong> - Records as stable feature</p>
</li>
<li><p><strong>Java 17 (JEP 409)</strong> - Sealed classes for controlled inheritance</p>
</li>
</ul>
<p><strong>Long-term support you can rely on:</strong> Oracle supports Java 17 until September 2029, giving teams a stable foundation for years to come.</p>
<p><strong>Migration-friendly:</strong> Java 17 maintains backward compatibility with Java 8. You can migrate existing code without changes, then gradually adopt new features where they add value.</p>
<p><strong>A strategic migration milestone:</strong><br />Java 17 is the first LTS release to consolidate all major improvements from Java 9–17, making it the recommended bridge for teams moving from Java 8. Most modern frameworks (like Spring Boot 3.x and Jakarta EE 10+) now require Java 17 as a baseline. Upgrading to Java 17 not only brings immediate language and performance benefits, but also ensures a smoother, lower-risk path to future LTS versions like Java 21 and 25.</p>
<hr />
<h2 id="heading-the-var-keyword-deep-dive">The var Keyword: Deep Dive</h2>
<h3 id="heading-what-is-var">What is var?</h3>
<p>The <code>var</code> keyword lets the compiler automatically infer the type of a local variable based on what you initialize it with. Introduced in Java 10 (JEP 286) and extended in Java 11 (JEP 323), var is Java's answer to one of its biggest criticisms: code verbosity.</p>
<p><strong>Heads up!</strong> <code>var</code> is <strong>NOT</strong> dynamic typing like JavaScript or Python. The type is determined at <strong>compile-time</strong> and never changes. If you write <code>var x = 42</code>, the compiler says "this is an int" and from that moment on, <code>x</code> is an int forever. You can't later assign a String or anything else to <code>x</code>. This is static typing with inference, not dynamic typing.</p>
<h3 id="heading-what-problem-does-var-solve">What problem does var solve?</h3>
<p>Before Java 10, we had to write the type twice on the same line:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Before Java 10 - repeating ourselves</span>
Map&lt;String, List&lt;Employee&gt;&gt; employeesByDept = <span class="hljs-keyword">new</span> HashMap&lt;String, List&lt;Employee&gt;&gt;();
</code></pre>
<p>See that? <code>Map&lt;String, List&lt;Employee&gt;&gt;</code> appears on the left (variable declaration) and on the right (constructor call). The compiler knows the type from the right side—why do we have to repeat it? This isn't just annoying, it makes refactoring harder. Change <code>List&lt;Employee&gt;</code> to <code>Set&lt;Employee&gt;</code>? You have to update it in two places.</p>
<p>Other languages solved this ages ago. C++ got <code>auto</code> in 2011, C# had <code>var</code> since 2007, Scala and Kotlin have had type inference from day one. Java, focused on backward compatibility and explicit typing, needed more time. But by 2018, with the Stream API and increasingly complex generic types, Oracle finally gave in.</p>
<h3 id="heading-why-does-var-matter">Why does var matter?</h3>
<p>Look at these two code snippets:</p>
<pre><code class="lang-java"><span class="hljs-comment">// Without var - eyes glaze over at the type</span>
Map&lt;String, List&lt;Transaction&gt;&gt; transactionsByCustomer = customers.stream()
    .collect(Collectors.groupingBy(
        Customer::getId,
        Collectors.mapping(Customer::getTransactions, Collectors.toList())
    ));

<span class="hljs-comment">// With var - focus on the logic, not type ceremony</span>
<span class="hljs-keyword">var</span> transactionsByCustomer = customers.stream()
    .collect(Collectors.groupingBy(
        Customer::getId,
        Collectors.mapping(Customer::getTransactions, Collectors.toList())
    ));
</code></pre>
<p>In the second version, your attention goes to the logic: we're grouping customers by ID and mapping to their transactions. The type is still there—the compiler knows it—but it's not cluttering the code.</p>
<p>Another thing: refactoring. Changing <code>ArrayList&lt;String&gt;</code> to <code>List&lt;String&gt;</code> across your codebase? Without var, you update every declaration. With var? Most declarations stay unchanged because the type comes from the right side.</p>
<p>Stream API benefits from var too. Chains like <code>.stream().map().filter().collect()</code> create complex intermediate types you never use directly. Why write them out?</p>
<h3 id="heading-how-does-type-inference-work">How does type inference work?</h3>
<p>The compiler looks at the right side (the initializer) and infers the <strong>most specific type</strong> that fits. For <code>var x = new ArrayList&lt;String&gt;()</code>, the inferred type is <code>ArrayList&lt;String&gt;</code>, <strong>NOT</strong> <code>List&lt;String&gt;</code>. This can be a gotcha—if you later want to assign a <code>LinkedList&lt;String&gt;</code> to <code>x</code>, it won't work because <code>x</code> is typed as <code>ArrayList&lt;String&gt;</code>.</p>
<p>Type inference happens at compile time. The bytecode generated for <code>var x = 42</code> is identical to <code>int x = 42</code>. At runtime there's no difference—var is pure syntactic sugar.</p>
<p>One more thing: Java doesn't automatically widen types. <code>var x = 127</code> gives you <code>int</code>, not <code>byte</code>, even though 127 fits in a byte. <code>var y = 3.14</code> is <code>double</code>, not <code>float</code>. If you need a specific primitive type, use an explicit declaration.</p>
<h3 id="heading-where-can-you-and-cant-you-use-var">Where can you (and can't you) use var?</h3>
<p><strong>You CAN</strong> use var for:</p>
<ul>
<li><p>Local variables (inside methods)</p>
</li>
<li><p>Loop variables (for, for-each)</p>
</li>
<li><p>Try-with-resources variables</p>
</li>
</ul>
<p><strong>You CANNOT</strong> use var for:</p>
<ul>
<li><p>Class fields</p>
</li>
<li><p>Method parameters (except lambda parameters in Java 11+)</p>
</li>
<li><p>Method return types</p>
</li>
</ul>
<p>This isn't an accident. Brian Goetz, Java's language architect, explained that var is meant to reduce local redundancy, not sacrifice API readability. Method signatures and class fields are public interfaces—they should be explicit. Local variables, used within a method's scope, benefit from inference because the context is immediate and limited.</p>
<h3 id="heading-when-to-use-var-and-when-not-to">When to use var (and when not to)?</h3>
<p><strong>Use var when</strong>:</p>
<ul>
<li><p>The type is obvious from the right side: <code>var user = new User("Alice")</code></p>
</li>
<li><p>The type is long and full of generics: <code>var config = new HashMap&lt;String, List&lt;Connection&gt;&gt;()</code></p>
</li>
<li><p>You're working with Stream API chains where intermediate types don't matter</p>
</li>
<li><p>The variable name clearly suggests the type: <code>var employeeCount = employees.size()</code></p>
</li>
</ul>
<p><strong>Don't use var when</strong>:</p>
<ul>
<li><p>The initializer doesn't reveal the type: <code>var data = fetchData()</code> (what type is this?)</p>
</li>
<li><p>You need a supertype for flexibility: <code>List&lt;String&gt; list = new ArrayList&lt;&gt;()</code> (you can change implementations later)</p>
</li>
<li><p>You're using literals that give unexpected types: <code>var b = 127</code> (that's int, not byte!)</p>
</li>
<li><p>The variable is used far from its declaration—explicit types help future readers</p>
</li>
</ul>
<hr />
<h2 id="heading-basic-syntax-and-use-cases">Basic Syntax and Use Cases</h2>
<h3 id="heading-where-you-can-use-var">Where You CAN Use var</h3>
<pre><code class="lang-java"><span class="hljs-comment">// 1. Local variables with initialization</span>
<span class="hljs-keyword">var</span> message = <span class="hljs-string">"Hello Java 17"</span>;
<span class="hljs-keyword">var</span> count = <span class="hljs-number">42</span>;
<span class="hljs-keyword">var</span> employees = <span class="hljs-keyword">new</span> ArrayList&lt;Employee&gt;();

<span class="hljs-comment">// 2. Enhanced for-loop</span>
<span class="hljs-keyword">var</span> numbers = List.of(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>);
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> num : numbers) {
    System.out.println(num);
}

<span class="hljs-comment">// 3. Traditional for-loop</span>
<span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10</span>; i++) {
    <span class="hljs-comment">// process</span>
}

<span class="hljs-comment">// 4. Try-with-resources</span>
<span class="hljs-keyword">try</span> (<span class="hljs-keyword">var</span> reader = <span class="hljs-keyword">new</span> BufferedReader(<span class="hljs-keyword">new</span> FileReader(<span class="hljs-string">"data.txt"</span>))) {
    <span class="hljs-comment">// read file</span>
}

<span class="hljs-comment">// 5. Lambda parameters (Java 11+)</span>
Predicate&lt;String&gt; notEmpty = (<span class="hljs-meta">@NonNull</span> <span class="hljs-keyword">var</span> s) -&gt; !s.isEmpty();
</code></pre>
<h3 id="heading-where-you-cannot-use-var">Where You CANNOT Use var</h3>
<pre><code class="lang-java"><span class="hljs-comment">// ❌ Class fields</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Example</span> </span>{
    <span class="hljs-keyword">var</span> field = <span class="hljs-string">"nope"</span>; <span class="hljs-comment">// Compilation error</span>
}

<span class="hljs-comment">// ❌ Method parameters</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">process</span><span class="hljs-params">(<span class="hljs-keyword">var</span> param)</span> </span>{ } <span class="hljs-comment">// Error</span>

<span class="hljs-comment">// ❌ Method return types</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">var</span> <span class="hljs-title">getValue</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> <span class="hljs-number">42</span>; } <span class="hljs-comment">// Error</span>

<span class="hljs-comment">// ❌ Without initialization</span>
<span class="hljs-keyword">var</span> x; <span class="hljs-comment">// Error: cannot infer type</span>

<span class="hljs-comment">// ❌ Null initialization</span>
<span class="hljs-keyword">var</span> y = <span class="hljs-keyword">null</span>; <span class="hljs-comment">// Error: null has no type</span>

<span class="hljs-comment">// ❌ Lambda/method reference initializer</span>
<span class="hljs-keyword">var</span> comparator = String::compareToIgnoreCase; <span class="hljs-comment">// Error</span>
</code></pre>
<hr />
<h2 id="heading-practical-examples">Practical Examples</h2>
<h3 id="heading-example-1-collections-and-stream-api">Example 1: Collections and Stream API</h3>
<p><strong>Concept:</strong> var reduces boilerplate when working with complex collection types and Stream API operations.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Demonstrates var with collections - eliminates verbose generic type declarations</span>

<span class="hljs-function"><span class="hljs-keyword">public</span> record <span class="hljs-title">Employee</span><span class="hljs-params">(String name, <span class="hljs-keyword">int</span> age, String department)</span> </span>{}

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
    <span class="hljs-comment">// Using var with List.of() - type is clearly List&lt;Employee&gt;</span>
    <span class="hljs-keyword">var</span> employees = List.of(
        <span class="hljs-keyword">new</span> Employee(<span class="hljs-string">"Alice"</span>, <span class="hljs-number">30</span>, <span class="hljs-string">"Engineering"</span>),
        <span class="hljs-keyword">new</span> Employee(<span class="hljs-string">"Bob"</span>, <span class="hljs-number">25</span>, <span class="hljs-string">"Marketing"</span>),
        <span class="hljs-keyword">new</span> Employee(<span class="hljs-string">"Charlie"</span>, <span class="hljs-number">35</span>, <span class="hljs-string">"Engineering"</span>)
    );

    <span class="hljs-comment">// Example 1: Filtering - var eliminates List&lt;Employee&gt; repetition</span>
    <span class="hljs-keyword">var</span> seniors = employees.stream()
        .filter(e -&gt; e.age() &gt;= <span class="hljs-number">28</span>)
        .toList();
    seniors.forEach(System.out::println);

    <span class="hljs-comment">// Example 2: Grouping - avoids verbose Map&lt;String, List&lt;Employee&gt;&gt;</span>
    <span class="hljs-keyword">var</span> byDepartment = employees.stream()
        .collect(Collectors.groupingBy(Employee::department));
    byDepartment.forEach((dept, emps) -&gt;
        System.out.println(dept + <span class="hljs-string">": "</span> + emps));
}
</code></pre>
<p><strong>Unit Test:</strong></p>
<pre><code class="lang-java"><span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should filter employees by minimum age using var with Stream API")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldFilterEmployeesByAge</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> employees = List.of(
        <span class="hljs-keyword">new</span> Employee(<span class="hljs-string">"Alice"</span>, <span class="hljs-number">30</span>, <span class="hljs-string">"Engineering"</span>),
        <span class="hljs-keyword">new</span> Employee(<span class="hljs-string">"Bob"</span>, <span class="hljs-number">25</span>, <span class="hljs-string">"Marketing"</span>),
        <span class="hljs-keyword">new</span> Employee(<span class="hljs-string">"Charlie"</span>, <span class="hljs-number">35</span>, <span class="hljs-string">"Engineering"</span>)
    );

    <span class="hljs-keyword">var</span> result = employees.stream()
        .filter(e -&gt; e.age() &gt;= <span class="hljs-number">28</span>)
        .toList();

    assertEquals(<span class="hljs-number">2</span>, result.size());
    assertTrue(result.stream().allMatch(e -&gt; e.age() &gt;= <span class="hljs-number">28</span>));
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">Employee[name=Alice, age=30, department=Engineering]
Employee[name=Charlie, age=35, department=Engineering]
Engineering: [Employee[name=Alice, age=30, department=Engineering], ...]
Marketing: [Employee[name=Bob, age=25, department=Marketing]]
</code></pre>
<p><strong>Key Insight</strong>: var eliminates repeating verbose generic types like <code>Map&lt;String, List&lt;Employee&gt;&gt;</code> while maintaining full compile-time type safety.</p>
<h3 id="heading-example-2-anonymous-classes-extended-api-access">Example 2: Anonymous Classes - Extended API Access</h3>
<p><strong>Concept:</strong> var enables access to custom methods in anonymous classes beyond the interface definition.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Demonstrates var's power with anonymous classes</span>
<span class="hljs-comment">// Access custom methods that aren't part of the interface</span>

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
    <span class="hljs-comment">// Traditional approach: type is Comparator&lt;String&gt;</span>
    <span class="hljs-comment">// Limited to interface methods only</span>
    Comparator&lt;String&gt; traditional = <span class="hljs-keyword">new</span> Comparator&lt;String&gt;() {
        <span class="hljs-meta">@Override</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">compare</span><span class="hljs-params">(String s1, String s2)</span> </span>{
            <span class="hljs-keyword">return</span> s2.length() - s1.length();
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getDescription</span><span class="hljs-params">()</span> </span>{
            <span class="hljs-keyword">return</span> <span class="hljs-string">"Sorts by length descending"</span>;
        }
    };
    <span class="hljs-comment">// traditional.getDescription(); // ❌ Compilation error!</span>

    <span class="hljs-comment">// With var: full access to anonymous class API!</span>
    <span class="hljs-keyword">var</span> enhanced = <span class="hljs-keyword">new</span> Comparator&lt;String&gt;() {
        <span class="hljs-meta">@Override</span>
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">compare</span><span class="hljs-params">(String s1, String s2)</span> </span>{
            <span class="hljs-keyword">return</span> s2.length() - s1.length();
        }

        <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getDescription</span><span class="hljs-params">()</span> </span>{
            <span class="hljs-keyword">return</span> <span class="hljs-string">"Sorts by length descending"</span>;
        }
    };

    <span class="hljs-comment">// ✅ Can call custom method!</span>
    System.out.println(enhanced.getDescription());

    <span class="hljs-comment">// Still works as Comparator</span>
    <span class="hljs-keyword">var</span> words = List.of(<span class="hljs-string">"Java"</span>, <span class="hljs-string">"is"</span>, <span class="hljs-string">"awesome"</span>);
    <span class="hljs-keyword">var</span> sorted = words.stream().sorted(enhanced).toList();
    System.out.println(<span class="hljs-string">"Sorted: "</span> + sorted);
}
</code></pre>
<p><strong>Unit Test:</strong></p>
<pre><code class="lang-java"><span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should enable access to custom methods in anonymous class through var")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldAccessCustomMethodInAnonymousClass</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> comparator = <span class="hljs-keyword">new</span> Comparator&lt;String&gt;() {
        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">compare</span><span class="hljs-params">(String s1, String s2)</span> </span>{
            <span class="hljs-keyword">return</span> s2.length() - s1.length();
        }
        <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">getDescription</span><span class="hljs-params">()</span> </span>{
            <span class="hljs-keyword">return</span> <span class="hljs-string">"Length descending comparator"</span>;
        }
    };

    assertEquals(<span class="hljs-string">"Length descending comparator"</span>, comparator.getDescription());
    assertTrue(comparator.compare(<span class="hljs-string">"short"</span>, <span class="hljs-string">"verylongword"</span>) &gt; <span class="hljs-number">0</span>);
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">Sorts by length descending
Sorted: [awesome, Java, is]
</code></pre>
<p><strong>Key Insight</strong>: Without var, the type is <code>Comparator&lt;String&gt;</code>, limiting you to interface methods. With var, the compiler infers the full anonymous class type, enabling access to custom methods.</p>
<h3 id="heading-example-3-intersection-types-unique-capability-of-var">Example 3: Intersection Types - Unique Capability of var</h3>
<p><strong>What are intersection types?</strong> A unique capability of var that allows declaring a variable implementing multiple interfaces simultaneously using lambda expressions. This is <strong>impossible</strong> with explicit type declarations.</p>
<pre><code class="lang-java"><span class="hljs-comment">// Demonstrates var's unique capability with intersection types</span>
<span class="hljs-comment">// Lambda implementing BOTH Welcome AND Goodbye interfaces simultaneously</span>

<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Welcome</span> </span>{
    <span class="hljs-function">String <span class="hljs-title">greet</span><span class="hljs-params">()</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">default</span> String <span class="hljs-title">getWelcomeMessage</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Welcome, "</span> + greet() + <span class="hljs-string">"!"</span>;
    }
}

<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Goodbye</span> </span>{
    <span class="hljs-function">String <span class="hljs-title">greet</span><span class="hljs-params">()</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">default</span> String <span class="hljs-title">getFarewellMessage</span><span class="hljs-params">()</span> </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Goodbye, "</span> + greet() + <span class="hljs-string">"!"</span>;
    }
}

<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>{
    <span class="hljs-comment">// var enables intersection type - IMPOSSIBLE with explicit types!</span>
    <span class="hljs-keyword">var</span> greeter = (Welcome &amp; Goodbye) () -&gt; <span class="hljs-string">"World"</span>;

    <span class="hljs-comment">// Access methods from BOTH interfaces</span>
    System.out.println(greeter.greet());              <span class="hljs-comment">// "World"</span>
    System.out.println(greeter.getWelcomeMessage());  <span class="hljs-comment">// "Welcome, World!"</span>
    System.out.println(greeter.getFarewellMessage()); <span class="hljs-comment">// "Goodbye, World!"</span>

    <span class="hljs-comment">// Verify instance checks</span>
    System.out.println(greeter <span class="hljs-keyword">instanceof</span> Welcome);   <span class="hljs-comment">// true</span>
    System.out.println(greeter <span class="hljs-keyword">instanceof</span> Goodbye);   <span class="hljs-comment">// true</span>
}
</code></pre>
<p><strong>Unit Test:</strong></p>
<pre><code class="lang-java"><span class="hljs-meta">@Test</span>
<span class="hljs-meta">@DisplayName("Should enable declaring variable with multiple interface types using var")</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">shouldEnableIntersectionType</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">var</span> greeter = (Welcome &amp; Goodbye) () -&gt; <span class="hljs-string">"World"</span>;

    assertEquals(<span class="hljs-string">"World"</span>, greeter.greet());
    assertEquals(<span class="hljs-string">"Welcome, World!"</span>, greeter.getWelcomeMessage());
    assertEquals(<span class="hljs-string">"Goodbye, World!"</span>, greeter.getFarewellMessage());
    assertTrue(greeter <span class="hljs-keyword">instanceof</span> Welcome);
    assertTrue(greeter <span class="hljs-keyword">instanceof</span> Goodbye);
}
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="lang-plaintext">World
Welcome, World!
Goodbye, World!
true
true
</code></pre>
<p><strong>Key Insight:</strong> You <strong>cannot</strong> write <code>Welcome &amp; Goodbye greeter = ...</code> with explicit types. This is a unique capability enabled only by var's type inference!</p>
<hr />
<h2 id="heading-common-pitfalls-and-limitations">Common Pitfalls and Limitations</h2>
<h3 id="heading-pitfall-1-type-too-specific">Pitfall 1: Type Too Specific</h3>
<pre><code class="lang-java"><span class="hljs-comment">// ❌ PROBLEM: Type is ArrayList&lt;String&gt;, not List&lt;String&gt;</span>
<span class="hljs-keyword">var</span> specificList = <span class="hljs-keyword">new</span> ArrayList&lt;String&gt;();
specificList.add(<span class="hljs-string">"Java"</span>);

<span class="hljs-comment">// Won't work - cannot assign LinkedList to ArrayList variable</span>
<span class="hljs-comment">// specificList = new LinkedList&lt;&gt;(); // ❌ Compilation error!</span>

<span class="hljs-comment">// ✅ SOLUTION: Use explicit interface type for flexibility</span>
List&lt;String&gt; flexibleList = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();
flexibleList = <span class="hljs-keyword">new</span> LinkedList&lt;&gt;(); <span class="hljs-comment">// ✅ Works</span>
</code></pre>
<h3 id="heading-pitfall-2-diamond-operator">Pitfall 2: Diamond Operator</h3>
<pre><code class="lang-java"><span class="hljs-comment">// ❌ Cannot use diamond without type info</span>
<span class="hljs-comment">// var list = new ArrayList&lt;&gt;(); // Compilation error</span>

<span class="hljs-comment">// ✅ Must specify type arguments</span>
<span class="hljs-keyword">var</span> list1 = <span class="hljs-keyword">new</span> ArrayList&lt;String&gt;();

<span class="hljs-comment">// ✅ Or use factory methods</span>
<span class="hljs-keyword">var</span> list2 = List.of(<span class="hljs-string">"Java"</span>, <span class="hljs-string">"Python"</span>);
</code></pre>
<h3 id="heading-pitfall-3-primitive-type-widening">Pitfall 3: Primitive Type Widening</h3>
<pre><code class="lang-java"><span class="hljs-keyword">var</span> byteValue = <span class="hljs-number">127</span>;    <span class="hljs-comment">// Type is int, NOT byte!</span>
<span class="hljs-keyword">var</span> floatValue = <span class="hljs-number">3.14</span>;  <span class="hljs-comment">// Type is double, NOT float!</span>

<span class="hljs-comment">// ❌ This fails:</span>
<span class="hljs-comment">// byte b = byteValue; // Error: incompatible types</span>

<span class="hljs-comment">// ✅ Use explicit types when size matters:</span>
<span class="hljs-keyword">byte</span> actualByte = <span class="hljs-number">127</span>;
<span class="hljs-keyword">float</span> actualFloat = <span class="hljs-number">3.14f</span>;
</code></pre>
<h3 id="heading-best-practices">Best Practices</h3>
<p>✅ <strong>DO</strong> use var when type is obvious:</p>
<pre><code class="lang-java"><span class="hljs-keyword">var</span> user = <span class="hljs-keyword">new</span> User(<span class="hljs-string">"Alice"</span>);
<span class="hljs-keyword">var</span> count = users.size();
</code></pre>
<p>✅ <strong>DO</strong> use var with long generic types:</p>
<pre><code class="lang-java"><span class="hljs-keyword">var</span> employeesByDept = <span class="hljs-keyword">new</span> HashMap&lt;String, List&lt;Employee&gt;&gt;();
</code></pre>
<p>❌ <strong>DON'T</strong> use var when type is unclear:</p>
<pre><code class="lang-java"><span class="hljs-keyword">var</span> data = fetchData(); <span class="hljs-comment">// What type is this?</span>
</code></pre>
<p>❌ <strong>DON'T</strong> use var when you need flexibility:</p>
<pre><code class="lang-java"><span class="hljs-keyword">var</span> list = <span class="hljs-keyword">new</span> ArrayList&lt;String&gt;(); <span class="hljs-comment">// Too specific</span>
</code></pre>
<hr />
<h2 id="heading-summary-and-next-steps">Summary and Next Steps</h2>
<h3 id="heading-key-takeaways">Key Takeaways</h3>
<ol>
<li><p><strong>var is type inference</strong>, NOT dynamic typing - types are resolved at compile-time</p>
</li>
<li><p><strong>Use var for long generic types</strong> and stream chains to improve readability</p>
</li>
<li><p><strong>Avoid var when type isn't obvious</strong> from context</p>
</li>
<li><p><strong>Remember limitations</strong>: Only local variables, requires initialization</p>
</li>
<li><p><strong>Java's var is conservative</strong> compared to other languages - by design</p>
</li>
</ol>
<h3 id="heading-coming-up-next">Coming Up Next</h3>
<p><strong>Part 2: Records (JEP 395)</strong> - Data classes in Java 16</p>
<ul>
<li><p>Eliminate boilerplate for DTOs</p>
</li>
<li><p>Canonical constructors and compact syntax</p>
</li>
<li><p>Records vs Lombok vs traditional POJOs</p>
</li>
<li><p>Immutability and pattern matching integration</p>
</li>
</ul>
<h3 id="heading-resources">Resources</h3>
<h4 id="heading-official-documentation">Official Documentation</h4>
<ul>
<li><p><strong>JEP 286 - Local-Variable Type Inference</strong>: <a target="_blank" href="https://openjdk.org/jeps/286">openjdk.org/jeps/286</a> The official JEP (Java Enhancement Proposal) that introduced the <code>var</code> keyword in Java 10. This document provides comprehensive technical details about the design decisions, implementation specifics, and rationale behind local variable type inference. Essential reading for understanding why certain limitations exist (e.g., why var works only for local variables).</p>
</li>
<li><p><strong>JEP 323 - Local-Variable Syntax for Lambda Parameters</strong>: <a target="_blank" href="https://openjdk.org/jeps/323">openjdk.org/jeps/323</a> Extends var usage to lambda parameters (Java 11), enabling uniform syntax with annotations: <code>(@NonNull var x) -&gt; x.process()</code>. This JEP explains how var maintains consistency with implicit lambda parameters while adding the ability to apply annotations.</p>
</li>
</ul>
<h4 id="heading-interactive-references">Interactive References</h4>
<ul>
<li><p><strong>Java Almanac - var Keyword</strong>: <a target="_blank" href="https://javaalmanac.io/features/var/">javaalmanac.io/features/var/</a> Interactive guide with runnable code examples showing var's capabilities and limitations. Includes visual timelines of when the feature was introduced and side-by-side comparisons with pre-Java 10 syntax. Perfect for quick reference and experimentation.</p>
</li>
<li><p><strong>Java Almanac - Method References</strong>: <a target="_blank" href="https://javaalmanac.io/features/method-references/">javaalmanac.io/features/method-references/</a> Complements var knowledge by showing how method references interact with type inference. Understanding this relationship helps avoid common pitfalls when combining var with functional programming constructs.</p>
</li>
</ul>
<h4 id="heading-code-repository">Code Repository</h4>
<ul>
<li><p><strong>GitHub Repository - blog-9mac-dev-code</strong>: <a target="_blank" href="https://github.com/dawid-swist/blog-9mac-dev-code/tree/main/blog-post-examples/java/2025-10-25-java17-features-every-senior-developer-should-know">github.com/dawid-swist/blog-9mac-dev-code</a> Contains all working examples from this article series, including JUnit tests and Gradle build configurations. Clone this repository to run examples locally and experiment with the code:</p>
<pre><code class="lang-bash">  git <span class="hljs-built_in">clone</span> https://github.com/dawid-swist/blog-9mac-dev-code.git
  <span class="hljs-built_in">cd</span> blog-post-examples/java/2025-10-25-java17-features-every-senior-developer-should-know
  ../../gradlew <span class="hljs-built_in">test</span>
</code></pre>
</li>
</ul>
<h4 id="heading-further-reading">Further Reading</h4>
<ul>
<li><p><strong>Style Guidelines for Local-Variable Type Inference in Java</strong>: <a target="_blank" href="https://openjdk.org/projects/amber/guides/lvti-style-guide">openjdk.org/projects/amber/guides/lvti-style-guide</a> Official OpenJDK style guide for using var effectively. Covers best practices, anti-patterns, and real-world scenarios where var improves (or harms) code readability. Written by Brian Goetz and Stuart Marks, this guide is the authoritative source for var usage conventions.</p>
</li>
<li><p><strong>Oracle Java SE Documentation - Local Variable Type Inference</strong>: <a target="_blank" href="https://docs.oracle.com/en/java/javase/17/language/local-variable-type-inference.html">docs.oracle.com/en/java/javase/17/language/local-variable-type-inference.html</a> Oracle's official documentation including detailed syntax rules, restrictions, and compilation behavior. Useful for understanding edge cases and compiler error messages related to var.</p>
</li>
</ul>
<h4 id="heading-community-resources">Community Resources</h4>
<ul>
<li><p><strong>Baeldung - Java 10 Local Variable Type Inference</strong>: Practical tutorial with common use cases and examples for Spring/JPA applications</p>
</li>
<li><p><strong>InfoQ - Java 10 Released</strong>: Historical context and industry reaction to the var keyword introduction</p>
</li>
<li><p><strong>Modern Java in Action (Book)</strong>: Chapter 3 covers lambda expressions and type inference, showing how var fits into Java's modern functional programming paradigm</p>
</li>
</ul>
<h2 id="heading-run-the-examples">Run the Examples</h2>
<p>All code examples from this article are available in the repository:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Run individual examples</span>
java -cp build/classes/java/main dev.nmac.blog.examples.java17.part1.VarCollectionsExample
java -cp build/classes/java/main dev.nmac.blog.examples.java17.part1.VarAnonymousClassExample
java -cp build/classes/java/main dev.nmac.blog.examples.java17.part1.VarIntersectionTypesExample
java -cp build/classes/java/main dev.nmac.blog.examples.java17.part1.VarLimitationsExample

<span class="hljs-comment"># Run tests</span>
./gradlew :blog-post-examples:java:2025-10-25-java17-features-every-senior-developer-should-know:<span class="hljs-built_in">test</span> --tests <span class="hljs-string">"*part1*"</span>
</code></pre>
<p><em>Written for</em> <a target="_blank" href="https://blog.9mac.dev"><em>blog.9mac.dev</em></a> <em>Part of the "Java 17 Features Every Senior Developer Should Know" series</em></p>
<p><strong>Next</strong>: <a target="_blank" href="https://blog.9mac.dev/java-17-features-every-senior-developer-should-know-part-2-records">Part 2: Records</a></p>
]]></content:encoded></item><item><title><![CDATA[Everything You Need to Know About the Spring Framework: History, Definitions, and Introduction]]></title><description><![CDATA[Definition of Spring Framework
Spring Framework is a comprehensive, lightweight, and open-source application development framework for Java platform. Created by Rod Johnson in 2003, Spring has evolved into one of the most popular frameworks for enter...]]></description><link>https://blog.9mac.dev/everything-you-need-to-know-about-the-spring-framework-history-definitions-and-introduction</link><guid isPermaLink="true">https://blog.9mac.dev/everything-you-need-to-know-about-the-spring-framework-history-definitions-and-introduction</guid><category><![CDATA[Springboot]]></category><category><![CDATA[Spring framework]]></category><category><![CDATA[Spring]]></category><category><![CDATA[Java]]></category><dc:creator><![CDATA[Dawid Świst]]></dc:creator><pubDate>Sun, 04 May 2025 22:00:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746643287794/1470da00-8ccd-41d5-a3e3-29678089fc13.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-definition-of-spring-framework">Definition of Spring Framework</h2>
<p>Spring Framework is a comprehensive, lightweight, and open-source application development framework for Java platform. Created by Rod Johnson in 2003, Spring has evolved into one of the most popular frameworks for enterprise-level Java applications. At its core, Spring Framework provides infrastructure support for developing Java applications, allowing developers to focus on business logic rather than technical complexities. It offers a well-organized architecture with several modules that can be used independently or together, making it highly flexible and adaptable to different project requirements.</p>
<p>Rod Johnson's journey toward creating Spring Framework began while he was working as a consultant on various Java enterprise projects in the late 1990s and early 2000s. He became increasingly frustrated with the complexity and heavyweight nature of Java Enterprise Edition (J2EE, now Jakarta EE) development. The standard approach at that time required extensive XML configuration, complex programming models, and tightly coupled components, making enterprise Java development unnecessarily difficult and time-consuming.</p>
<p>In 2002, Johnson published the book "Expert One-on-One J2EE Design and Development," where he introduced his ideas for a simpler approach to Java enterprise applications. The book included a framework that would later evolve into Spring. His motivation was clear: to create a lightweight alternative to the cumbersome J2EE stack that would address real-world problems faced by developers. Johnson believed that enterprise applications could be simpler, more testable, and better designed through the use of Plain Old Java Objects (POJOs), dependency injection, and aspect-oriented programming.</p>
<p>Johnson further solidified his ideas in his 2004 follow-up book, "Expert One-on-One J2EE Development without EJB." This influential work directly challenged the then-conventional wisdom that Enterprise JavaBeans (EJB) were essential for enterprise applications. The book demonstrated how developers could build robust, scalable applications without the complexity and overhead of EJB, instead relying on simpler POJOs managed by what was now becoming known as the Spring Framework. This publication was a turning point for enterprise Java development. It clearly explained the technical and practical benefits of the Spring approach and showed that the heavy EJB model wasn't needed for most enterprise applications. The book also included expanded code samples and practical patterns that would become foundational elements of Spring's design philosophy, helping to accelerate adoption of the framework across the Java development community.</p>
<p>The framework was officially released as open-source in 2003, named "Spring" to represent a fresh start or "springtime" for Java development after the "winter" of complex J2EE approaches. What began as one developer's solution to practical problems quickly gained traction in the Java community because it resonated with developers who faced similar challenges in their daily work.</p>
<h2 id="heading-core-principles-and-design-philosophy">Core Principles and Design Philosophy</h2>
<p>Spring Framework is built on several fundamental principles that define its approach to application development:</p>
<ol>
<li><p><strong>Inversion of Control (IoC)</strong>: Perhaps the most central concept in Spring, IoC inverts the flow of control compared to traditional programming. Instead of the application code controlling the flow and calling external libraries or frameworks, Spring takes control of the application flow and calls the developer's code when needed. This reduces coupling between components and makes the system more maintainable.</p>
</li>
<li><p><strong>Dependency Injection (DI)</strong>: As an implementation of IoC, Dependency Injection is a design pattern where objects receive their dependencies rather than creating them. Spring manages object creation and "injects" objects into each other when needed, eliminating the need for developers to handle object instantiation and lifecycle management manually.</p>
</li>
<li><p><strong>Aspect-Oriented Programming (AOP)</strong>: Spring supports AOP, allowing developers to separate cross-cutting concerns (like logging, security, or transactions) from business logic. This promotes cleaner code organization by modularizing functionality that would otherwise be scattered throughout the application.</p>
</li>
<li><p><strong>Modularity</strong>: Spring is organized into about 20 modules, each addressing specific functionality. Developers can choose which modules to use based on their requirements, making Spring lightweight and avoiding unnecessary overhead.</p>
</li>
<li><p><strong>Framework Integration</strong>: Spring is designed to work seamlessly with other frameworks and libraries, rather than requiring developers to use only Spring components. This non-invasive approach allows for gradual adoption and integration with existing systems.</p>
</li>
<li><p><strong>Testability</strong>: By promoting loose coupling through dependency injection, Spring makes applications inherently more testable. Components can be easily mocked or stubbed for unit testing.</p>
</li>
<li><p><strong>Simplicity and Productivity</strong>: Spring aims to simplify Java development by reducing boilerplate code through annotations, templates, and other abstractions, thereby increasing developer productivity.</p>
</li>
</ol>
<p>These principles combine to form a framework that addresses many common challenges in enterprise application development: managing component dependencies, handling cross-cutting concerns, supporting different data access technologies, and providing comprehensive integration capabilities – all while maintaining a focus on code quality and developer experience.</p>
<h2 id="heading-short-history-and-spring-framework-releases">Short history and Spring Framework releases</h2>
<p>Spring Framework has evolved significantly since its initial release, with each major version introducing important improvements and new features:</p>
<ul>
<li><p><strong>Spring 1.0</strong> (March 2004): The first official release established the core functionality including the IoC container, AOP support, JDBC abstraction, and transaction management. This version proved the viability of Johnson's approach.</p>
</li>
<li><p><strong>Spring 2.0</strong> (October 2006): This release introduced XML namespaces for easier configuration, enhanced AOP capabilities, and support for Java 5 features like annotations and generics. It also marked the beginning of Spring's transition from a purely configuration-based approach to a more annotation-driven model.</p>
</li>
<li><p><strong>Spring 2.5</strong> (November 2007): Annotation-based configuration was significantly expanded, reducing the need for XML configuration. The @Component, @Service, @Repository, and @Controller annotations were introduced, allowing for classpath scanning and automatic bean registration.</p>
</li>
<li><p><strong>Spring 3.0</strong> (December 2009): This version brought Java 5 as a minimum requirement and included full support for Java 6 features. It introduced the Spring Expression Language (SpEL), a new REST support, and enhanced the annotation-based configuration model with the @Configuration annotation.</p>
</li>
<li><p><strong>Spring 3.1</strong> (December 2011): Added significant improvements to the IoC container with new bean definition profiles and the introduction of the Environment abstraction.</p>
</li>
<li><p><strong>Spring 4.0</strong> (December 2013): Added full support for Java 8 features, WebSocket programming, and improved REST capabilities. It also introduced conditional bean configuration using @Conditional annotations.</p>
</li>
<li><p><strong>Spring 4.3</strong> (June 2016): The last version in the 4.x line, introducing further refinements to annotation handling and dependency injection.</p>
</li>
<li><p><strong>Spring 5.0</strong> (September 2017): A major milestone that added reactive programming support through the Spring WebFlux module, based on Project Reactor. This version required Java 8 as a minimum and was compatible with Java 9. It introduced functional programming models alongside traditional annotations.</p>
</li>
<li><p><strong>Spring 5.3</strong> (October 2020): The latest major release in the 5.x line, enhancing compatibility with newer Java versions and refining reactive programming support.</p>
</li>
<li><p><strong>Spring 6.0</strong> (November 2022): A significant update that requires Java 17, embraces Jakarta EE 9+ (rather than javax namespaces), and further enhances the reactive programming model. It includes performance optimizations and improved support for GraalVM native images.</p>
</li>
<li><p><strong>Spring 6.1</strong> (November 2023): Introduced refinements to virtual threads, AOT (Ahead-of-Time) compilation, and optimizations for cloud-native applications.</p>
</li>
<li><p><strong>Spring 6.2</strong> (May 2024): The most recent version, featuring enhanced support for Java 21 features, further improvements to virtual thread utilization, expanded native image capabilities with GraalVM, and refinements to the observability API for better monitoring and tracing. This release also includes optimizations for startup time and memory usage, particularly important for containerized and serverless deployments.</p>
</li>
</ul>
<p>Throughout its evolution, Spring has consistently adapted to changing industry trends while maintaining its core principles. The project is now maintained by VMware (previously Pivotal and before that, SpringSource), with a large and active community contributing to its development. The framework has expanded far beyond its original scope to include a rich ecosystem of related projects, collectively known as the Spring Portfolio, covering areas such as security, data, cloud computing, batch processing, and microservices.</p>
<h2 id="heading-spring-framework-core-and-its-relationship-with-spring-sub-projects">Spring Framework Core and Its Relationship with spring sub projects</h2>
<p>Spring Framework Core serves as the foundation for the entire Spring ecosystem. It provides the essential infrastructure that all other Spring projects build upon. At its heart is the IoC container, which manages bean creation, configuration, and wiring. This container uses metadata (XML, annotations, or Java code) to determine how beans should be instantiated, configured, and assembled.</p>
<p>The Core module includes several key components:</p>
<ul>
<li><p><a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-beanfactory"><strong>BeanFactory</strong></a>: The basic IoC container that provides the fundamental bean management capabilities.</p>
</li>
<li><p><a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-client"><strong>ApplicationContext</strong></a>: An enhanced version of BeanFactory providing additional enterprise-specific functionality.</p>
</li>
<li><p><a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions"><strong>SpEL (Spring Expression Language)</strong></a>: A powerful expression language for querying and manipulating object graphs at runtime.</p>
</li>
<li><p><a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#resources"><strong>Resource abstraction</strong></a>: A consistent mechanism to access resources regardless of their location.</p>
</li>
<li><p><a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#validation"><strong>Validation, Data</strong></a> <a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-beanfactory"><strong>Binding, a</strong></a><a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#validation"><strong>nd Type Conversion</strong></a>: Tools for converting between different value types and validating dat<a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-beanfactory">a.</a></p>
</li>
<li><p><a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-beanfactory"><strong>AOP M</strong></a><a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-client"><strong>odule</strong>: Impl</a>ementation of aspect-oriented programming, enabling cross-cutting concerns.</p>
</li>
<li><p><a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-api-advice-types"><strong>Ins</strong></a><a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factory-client"><strong>trumentati</strong></a><a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-beanfactory"><strong>on</strong>: Class i</a>nstru<a target="_blank" href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions">mentation support and classloader</a> implementations.</p>
</li>
</ul>
<p>Spring Framework Core acts as the central hub that connects all other Spring projects. It provides the common threading, exception handling, utility methods, and other infrastructure that enables the cohesive functioning of the ecosystem. When other projects like Spring Boot, Spring Data, or Spring Security are added to an application, they all integrate with and build upon this core foundation.</p>
<p>The relationship between Spring Core and other projects is symbiotic. While Spring Core can function independently, the additional projects extend its capabilities for specific domains or use cases. For example, Spring Boot leverages Spring Core's autowiring and configuration capabilities but adds auto-configuration and embedded servers to simplify application setup. Similarly, Spring Security uses Spring Core's AOP features to implement method-level security.</p>
<p>This modular design allows developers to choose the specific Spring components they need without being forced to adopt the entire ecosystem. However, when used together, these components integrate seamlessly due to their shared foundation in Spring Core principles. Spring components they need without being forced to adopt the entire ecosystem. However, when used together, these components integrate seamlessly due to their shared foundation in Spring Core principles.</p>
<h2 id="heading-the-spring-ecosystem-key-projects-and-components">The Spring Ecosystem: Key Projects and Components</h2>
<p>The Spring Portfolio has grown substantially over the years, evolving into a comprehensive ecosystem of projects that cover nearly every aspect of application development. Here are the major projects within the Spring ecosystem:</p>
<ol>
<li><p><a target="_blank" href="https://spring.io/projects/spring-framework"><strong>Spring Framework</strong></a>: The foundation of all Spring projects, providing the core IoC container, AOP capabilities, data access framework, transaction management, web MVC framework, and more. It's the essential base upon which all other Spring projects ar<a target="_blank" href="https://spring.io/projects/spring-framework">e built.</a></p>
</li>
<li><p><a target="_blank" href="https://spring.io/projects/spring-framework"><strong>Spri</strong></a><a target="_blank" href="https://spring.io/projects/spring-boot"><strong>ng Boot</strong></a>: Simplifies the initial setup and development of Spring applications by providing auto-configuration, starter dependencies, and embedded servers. It takes an opinionated view of building production-ready applications, allowing dev<a target="_blank" href="https://spring.io/projects/spring-boot">elopers to</a> get started with minimal configuration.</p>
</li>
<li><p><a target="_blank" href="https://spring.io/projects/spring-data"><strong>Spring Data</strong></a>: Provides a consistent approach to data access, supporting various data stores including relational databases, NoSQL databases, map-reduce frameworks, and cloud-based data services. Notable sub-projects include Spring Data JPA, S<a target="_blank" href="https://spring.io/projects/spring-data">pring Data</a> MongoDB, Spring Data Redis, and more.</p>
</li>
<li><p><a target="_blank" href="https://spring.io/projects/spring-security"><strong>Spring Security</strong></a>: Offers comprehensive security services for Java EE-based enterprise software applications. It handles authentication, authorization, protection against attacks, and integrates with various security providers and standards.</p>
</li>
<li><p><a target="_blank" href="https://spring.io/projects/spring-cloud"><strong>S</strong></a><a target="_blank" href="https://spring.io/projects/spring-security"><strong>pring Cloud</strong>: Pr</a>ovides tools for building and deploying distributed systems in cloud environments. It includes service discovery, configuration management, circuit breakers, intelligent routing, and more, making it easier to implement micro<a target="_blank" href="https://spring.io/projects/spring-cloud">services arc</a>hitectures.</p>
</li>
<li><p><a target="_blank" href="https://spring.io/projects/spring-batch"><strong>Spring Batch</strong></a>: Offers a robust batch processing framework designed for high-volume data operations, including logging/tracing, transaction management, job processing statistics, job restart, and resource management.</p>
</li>
<li><p><a target="_blank" href="https://spring.io/projects/spring-integration"><strong>Spring Integration</strong></a>: I<a target="_blank" href="https://spring.io/projects/spring-batch">mplements en</a>terprise integration patterns to facilitate message-based applications. It enables lightweight messaging within Spring-based applications and supports integration with external systems.</p>
</li>
<li><p><a target="_blank" href="https://docs.spring.io/spring-framework/reference/web/webflux.html"><strong>Spring WebFlux</strong></a>: Provid<a target="_blank" href="https://spring.io/projects/spring-integration">es reactive progra</a>mming support for web applications, offering a non-blocking alternative to Spring MVC. It's built on Project Reactor and designed for event-loop execution models.</p>
</li>
<li><p><a target="_blank" href="https://docs.spring.io/spring-framework/reference/web/webmvc.html"><strong>Spring Web MVC</strong></a>: The traditional w<a target="_blank" href="https://docs.spring.io/spring-framework/reference/web/webflux.html">eb framework b</a>uilt on the Servlet API, providing model-view-controller architecture and ready components for building web applications.</p>
</li>
<li><p><a target="_blank" href="https://spring.io/projects/spring-hateoas"><strong>Spring HATEOAS</strong></a>: Simplifies creating REST representations that foll<a target="_blank" href="https://docs.spring.io/spring-framework/reference/web/webmvc.html">ow the HATEOAS</a> (Hypermedia as the Engine of Application State) principle.</p>
</li>
<li><p><a target="_blank" href="https://spring.io/projects/spring-session"><strong>Spring Session</strong></a>: Provides a way to manage user session information without being tied to an app<a target="_blank" href="https://spring.io/projects/spring-hateoas">lication conta</a>iner-specific solution.</p>
</li>
<li><p><a target="_blank" href="https://spring.io/projects/spring-amqp"><strong>Spring AMQP</strong></a>: Supports the Advanced Message Queuing Protocol, making it easy to develop messaging solu<a target="_blank" href="https://spring.io/projects/spring-session">tions.</a></p>
</li>
<li><p><a target="_blank" href="https://spring.io/projects/spring-session"><strong>Spri</strong></a><a target="_blank" href="https://spring.io/projects/spring-kafka"><strong>ng for Apache Kafka</strong></a>: Provides integration with the Kafka distributed streaming platform.</p>
</li>
<li><p><a target="_blank" href="https://spring.io/projects/spring-graphql"><strong>Spring GraphQL</strong></a>: Offers in<a target="_blank" href="https://spring.io/projects/spring-amqp">tegration w</a>ith the GraphQL query language for APIs.</p>
</li>
<li><p><a target="_blank" href="https://docs.spring.io/spring-native/docs/current/reference/htmlsingle/"><strong>Spring Native</strong></a>: Enables compiling Spring applications to native exec<a target="_blank" href="https://spring.io/projects/spring-kafka">utables using GraalVM f</a>or improved startup time and reduced memory usage.</p>
</li>
<li><p><a target="_blank" href="https://spring.io/projects/spring-shell"><strong>Spring Sh</strong></a><a target="_blank" href="https://spring.io/projects/spring-graphql"><strong>ell</strong>: Helps in</a> building command-line applications.</p>
</li>
<li><p><a target="_blank" href="https://spring.io/projects/spring-ldap"><strong>Spring LDAP</strong></a>: Simplifies L<a target="_blank" href="https://docs.spring.io/spring-native/docs/current/reference/htmlsingle/">DAP programmi</a>ng.</p>
</li>
<li><p><a target="_blank" href="https://spring.io/projects/spring-mobile"><strong>Spring Mobile</strong></a>: Simplifies the development of mobile web applications.</p>
</li>
<li><p><a target="_blank" href="https://spring.io/projects/spring-android"><strong>Spring for Android</strong></a>: Provides key Spring component<a target="_blank" href="https://spring.io/projects/spring-shell">s for use in</a> Android applications.</p>
</li>
<li><p><a target="_blank" href="https://spring.io/projects/spring-ws"><strong>Spring Web Services</strong></a>: F<a target="_blank" href="https://spring.io/projects/spring-ldap">acilitates</a> the creation of contract-first SOAP <a target="_blank" href="https://spring.io/projects/spring-mobile">web services</a>.</p>
</li>
<li><p><a target="_blank" href="https://spring.io/projects/spring-vault"><strong>Spring Vault</strong></a>: Provides integration with HashiCorp Vault for <a target="_blank" href="https://spring.io/projects/spring-android">secrets managemen</a>t.</p>
</li>
<li><p><a target="_blank" href="https://spring.io/projects/spring-authorization-server"><strong>Spring Authorization Server</strong></a>: Implements the OAuth 2.1 Authoriza<a target="_blank" href="https://spring.io/projects/spring-ws">tion Server specifi</a>cation.</p>
</li>
</ol>
<p>These projects share common patterns, principles<a target="_blank" href="https://spring.io/projects/spring-vault">, and even c</a>ode from Spring Framework Core, creating a cohesive ecosystem where components work t<a target="_blank" href="https://spring.io/projects/spring-authorization-server">ogether seamlessly. The mod</a>ularity of Spring means that developers can incorporate only the projects they need, making it adaptable to various application requirements and architectural styles.</p>
<p>What makes the Spring ecosystem particularly powerful is this balance between integration and independence. Each project can be used on its own, but when used together, they provide a comprehensive platform for building enterprise applications with minimal friction between components. This harmonious relationship between Spring Core and its satellite projects has been a key factor in Spring's enduring popularity and relevance in the Java development world.</p>
<h2 id="heading-spring-boot-simplifying-spring-development">Spring Boot: Simplifying Spring Development</h2>
<p>Spring Boot represents a revolutionary approach to developing Spring applications. Launched in 2014, it was created to address the growing complexity of configuration required to set up Spring applications, especially as the Spring ecosystem expanded. Spring Boot fundamentally changes how developers interact with the Spring Framework by embracing convention over configuration and providing an opinionated view of application setup.</p>
<h3 id="heading-key-features-and-principles">Key Features and Principles</h3>
<ol>
<li><p><a target="_blank" href="https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.auto-configuration"><strong>Auto-configuration</strong></a>: Spring Boot automatically configures your application based on the dependencies you have added to the project. For example, if you include the MySQL driver, Spring Boot automatically configures a data source for MySQL without requiring explicit configuration.</p>
</li>
<li><p><a target="_blank" href="https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started.html#getting-started.first-application.executable-jar"><strong>Standalone Applications</strong></a>: Spring Boot applications can be run as standalone JARs with an embedded server, eliminating the need for external application server deployment. This simplifies the deployment process significantly.</p>
</li>
<li><p><a target="_blank" href="https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.build-systems.starters"><strong>Starter Dependencies</strong></a>: Spring Boot offers carefully curated "starter" dependency descriptors that greatly simplify Maven/Gradle configuration. Instead of individually specifying multiple dependencies, developers can include a single starter like <code>spring-boot-starter-web</code> that pulls in all related dependencies for web application development.</p>
</li>
<li><p><a target="_blank" href="https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html"><strong>Production-Ready Features</strong></a>: Built-in capabilities like metrics, health checks, and externalized configuration make Spring Boot applications immediately suitable for production deployment.</p>
</li>
</ol>
<h3 id="heading-spring-boot-vs-traditional-spring-framework">Spring Boot vs. Traditional Spring Framework</h3>
<p>While traditional Spring applications require extensive configuration through XML or Java config, Spring Boot reduces this overhead dramatically. A simple REST service that might require dozens of lines of configuration in traditional Spring can be created with just a few annotations in Spring Boot.</p>
<p>The benefits of this approach include:</p>
<ul>
<li><p><strong>Faster Development</strong>: Less time spent on boilerplate configuration means more time for business logic.</p>
</li>
<li><p><strong>Consistent Conventions</strong>: Standardized approaches to common tasks reduce decision fatigue.</p>
</li>
<li><p><strong>Simplified Dependency Management</strong>: Starter dependencies ensure compatible versions of related libraries.</p>
</li>
</ul>
<h2 id="heading-limitations-and-challenges-of-spring-framework-and-spring-boot">Limitations and Challenges of Spring Framework and Spring Boot</h2>
<p>While Spring Framework and Spring Boot offer numerous advantages, they are not without their drawbacks. Understanding these limitations is essential for making informed decisions about when and how to utilize these technologies.</p>
<h3 id="heading-limitations-of-spring-framework">Limitations of Spring Framework</h3>
<ol>
<li><p><strong>Steep Learning Curve</strong>: Despite its aim to simplify Java development, Spring's extensive ecosystem can be overwhelming for beginners. The framework encompasses numerous concepts, patterns, and modules that require time to master.</p>
</li>
<li><p><strong>Configuration Complexity</strong>: Although Spring has moved away from XML configuration toward annotation-based and Java-based configuration, large applications can still end up with complex configuration structures that are difficult to maintain and debug.</p>
</li>
<li><p><strong>Performance Overhead</strong>: The dynamic proxies and reflection used for dependency injection and AOP can introduce performance overhead, particularly during application startup. This is especially noticeable in smaller applications where the benefits may not outweigh the costs.</p>
</li>
<li><p><strong>Memory Consumption</strong>: Spring applications typically consume more memory than lightweight alternatives due to the framework's comprehensive feature set and the objects required to support its infrastructure.</p>
</li>
<li><p><strong>Dependency Hell</strong>: With the vast Spring ecosystem, managing dependencies and ensuring compatibility between different Spring modules and versions can become challenging, especially in large projects.</p>
</li>
<li><p><strong>Annotation Proliferation</strong>: The heavy use of annotations can lead to code that's difficult to understand at a glance, as the annotations may obscure the actual business logic of the application.</p>
</li>
<li><p><strong>Debugging Challenges</strong>: When issues arise, the layers of abstraction provided by Spring can make debugging more difficult, as the problem may be buried deep within the framework's internals.</p>
</li>
</ol>
<h3 id="heading-limitations-of-spring-boot">Limitations of Spring Boot</h3>
<ol>
<li><p><strong>"Magic" Configuration</strong>: While auto-configuration is a key selling point, it can also be a double-edged sword. Developers may not understand what's happening behind the scenes, leading to unexpected behavior that's difficult to troubleshoot.</p>
</li>
<li><p><strong>Limited Control</strong>: Spring Boot's opinionated approach works well for standard use cases but can be restrictive for highly customized applications. Overriding default configurations sometimes requires deep knowledge of Spring Boot's internals.</p>
</li>
<li><p><strong>Large JAR Files</strong>: Spring Boot's embedded server approach results in larger deployment artifacts (fat JARs), which can be problematic in environments with limited resources or bandwidth constraints.</p>
</li>
<li><p><strong>Startup Time</strong>: Although improvements have been made in recent versions, Spring Boot applications can have relatively slow startup times compared to more lightweight frameworks, which can impact development cycles and deployment in certain environments.</p>
</li>
<li><p><strong>Version Lock-in</strong>: Spring Boot starters bundle specific versions of dependencies, which provides consistency but may prevent using newer versions of individual libraries without significant configuration changes.</p>
</li>
<li><p><strong>Microservice Bloat</strong>: While Spring Boot is often used for microservices, the framework brings a considerable amount of overhead that can contradict the microservice philosophy of being lightweight and focused.</p>
</li>
<li><p><strong>Upgrade Challenges</strong>: Major version upgrades of Spring Boot often require significant changes to applications, particularly when auto-configuration behavior changes or dependencies are updated.</p>
</li>
<li><p><strong>Resource Utilization</strong>: Spring Boot applications typically require more CPU and memory resources than minimalist frameworks, which can increase operational costs, especially in cloud environments.</p>
</li>
</ol>
<h3 id="heading-framework-alternatives">Framework Alternatives</h3>
<ol>
<li><p><a target="_blank" href="https://quarkus.io/"><strong>Quarkus</strong></a>: Developed by Red Hat, Quarkus is a Kubernetes-native Java framework tailored for GraalVM and OpenJDK HotSpot. Key advantages include:</p>
<ul>
<li><p>Significantly faster startup time and lower memory footprint</p>
</li>
<li><p>Optimized for containerized environments and serverless architectures</p>
</li>
<li><p>Extensive support for native compilation via GraalVM</p>
</li>
<li><p>Compatible with many Jakarta EE and MicroProfile standards</p>
</li>
<li><p>Developer-friendly with features like live coding (hot reload)</p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://micronaut.io/"><strong>Micronaut</strong></a>: Created by the team behind Grails, Micronaut is designed to overcome the limitations of Spring and similar frameworks:</p>
<ul>
<li><p>Avoids reflection by using ahead-of-time (AOT) compilation</p>
</li>
<li><p>Extremely low memory footprint and fast startup times</p>
</li>
<li><p>Built-in cloud-native features and serverless application support</p>
</li>
<li><p>Dependency injection that doesn't use proxies or reflection</p>
</li>
<li><p>Integrated reactive programming model</p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://helidon.io/"><strong>Helidon</strong></a>: Oracle's microservices framework offering both reactive and imperative programming models:</p>
<ul>
<li><p>Lightweight, with minimal dependencies</p>
</li>
<li><p>MicroProfile implementation for enterprise Java features</p>
</li>
<li><p>Strong reactive programming support with Helidon Reactive</p>
</li>
<li><p>Configuration system inspired by Kubernetes</p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://vertx.io/"><strong>Vert.x</strong></a>: A toolkit for building reactive applications on the JVM:</p>
<ul>
<li><p>Event-driven, non-blocking architecture</p>
</li>
<li><p>Polyglot support (Java, Kotlin, JavaScript, Ruby, and more)</p>
</li>
<li><p>Highly performant with a small footprint</p>
</li>
<li><p>Modular design allowing use of only what's needed</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-traditional-java-ee-jakarta-ee">Traditional Java EE / Jakarta EE</h3>
<ol start="5">
<li><p><a target="_blank" href="https://jakarta.ee/"><strong>Jakarta EE</strong></a> (formerly Java EE): The enterprise Java standard:</p>
<ul>
<li><p>Industry-standard specifications with multiple implementations</p>
</li>
<li><p>Comprehensive enterprise features without vendor lock-in</p>
</li>
<li><p>Strong backward compatibility focus</p>
</li>
<li><p>Well-suited for organizations with existing Java EE expertise</p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://www.payara.fish/"><strong>Payara</strong></a>: An enhanced Jakarta EE application server derived from GlassFish:</p>
<ul>
<li><p>Strong focus on production readiness and support</p>
</li>
<li><p>Cloud-native features with Payara Micro</p>
</li>
<li><p>Optimized for microservices and containerized deployments</p>
</li>
<li><p>Compatible with Jakarta EE standards</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-lightweight-alternatives">Lightweight Alternatives</h3>
<ol start="7">
<li><p><a target="_blank" href="https://javalin.io/"><strong>Javalin</strong></a>: A simple web framework for Java and Kotlin:</p>
<ul>
<li><p>Minimalist approach with a clear API</p>
</li>
<li><p>Very low overhead and fast startup</p>
</li>
<li><p>Excellent for microservices and REST APIs</p>
</li>
<li><p>Seamless Kotlin integration</p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://sparkjava.com/"><strong>Spark Java</strong></a>: A micro-framework for creating web applications:</p>
<ul>
<li><p>Expressive, lambda-based route definitions</p>
</li>
<li><p>Minimal footprint and dependencies</p>
</li>
<li><p>Focus on simplicity and developer productivity</p>
</li>
<li><p>Well-suited for small to medium APIs and applications</p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://www.dropwizard.io/"><strong>Dropwizard</strong></a>: Combines stable Java libraries into a simple package:</p>
<ul>
<li><p>Out-of-the-box support for configuration, metrics, and logging</p>
</li>
<li><p>Fast development cycle with built-in testing support</p>
</li>
<li><p>Good performance characteristics</p>
</li>
<li><p>RESTful web services with minimal setup</p>
</li>
</ul>
</li>
</ol>
<h2 id="heading-summary">Summary</h2>
<p>In our upcoming series, we'll provide hands-on exploration of Spring Framework's key functionalities through practical coding examples. We'll cover essential concepts including dependency injection, AOP, data access, MVC, WebFlux, and security implementations.</p>
<p>In this article, I wanted to introduce the definition of Spring Framework, its applications, structure, advantages, and disadvantages. I presented a comprehensive picture of this popular Java framework - from its historical beginnings as an alternative to heavyweight J2EE, through key principles like IoC and DI, to the modern approach introduced by Spring Boot. I also discussed the limitations of both technologies and presented alternative solutions available in the Java ecosystem, which should help in making informed technological decisions. I invite you to follow our series of articles on Spring Framework, where together we'll discover the powerful capabilities of this ecosystem in practical applications.</p>
]]></content:encoded></item><item><title><![CDATA[Journey from Java to Scala 3.x: Introducing Our Latest Blog Series]]></title><description><![CDATA[Intro
I am pleased to introduce a new series of articles on my blog about the Scala programming language. In this series, I will examine Scala from the perspective of a Java programmer to highlight the differences and similarities between the two lan...]]></description><link>https://blog.9mac.dev/journey-from-java-to-scala-3x-introducing-our-latest-blog-series</link><guid isPermaLink="true">https://blog.9mac.dev/journey-from-java-to-scala-3x-introducing-our-latest-blog-series</guid><category><![CDATA[Scala]]></category><category><![CDATA[scala3, ]]></category><dc:creator><![CDATA[Dawid Świst]]></dc:creator><pubDate>Mon, 28 Apr 2025 20:04:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/Bgoceb_kc0k/upload/91fea9090161e49114dd6ba1b14b15a7.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-intro">Intro</h2>
<p>I am pleased to introduce a new series of articles on my blog about the Scala programming language. In this series, I will examine Scala from the perspective of a Java programmer to highlight the differences and similarities between the two languages. The primary focus will be on the latest version, Scala 3.x, which includes numerous updates and enhancements.</p>
<p>The initial articles will serve as a cheat sheet for Scala 3.x. I will explain the fundamental tools, their purposes, and how to install them across various operating systems. This will assist both novice and experienced programmers in quickly getting started with Scala.</p>
<p>I have been interested in Scala since 2010 and have observed its development over the years. Although it is not as popular as Java or Python, Scala offers many interesting features and achievements. The language does present some challenges—it can be difficult to learn, compilation can be slow, and different versions do not always work well together. Nonetheless, Scala's rich features have inspired many changes in Java. Java has adopted ideas from Scala, such as lambda expressions, the Stream API, the 'var' keyword, and record classes.</p>
<h2 id="heading-key-scala-features-and-tools">Key Scala Features and Tools</h2>
<p>In this series, I aim to describe these significant Scala features and tools:</p>
<p><strong>Language Features:</strong></p>
<ul>
<li><p><strong>Pattern Matching</strong>: A robust method for simultaneously checking data structures and extracting values.</p>
</li>
<li><p><strong>Case Classes</strong>: Simplified classes ideal for modeling data with less code than Java beans.</p>
</li>
<li><p><strong>Type Inference</strong>: The compiler often determines the type, eliminating the need for explicit type declarations.</p>
</li>
<li><p><strong>Immutability</strong>: Objects that remain unchanged after creation, enhancing code safety in multi-threaded applications.</p>
</li>
<li><p><strong>Traits</strong>: Comparable to Java interfaces but offering greater flexibility.</p>
</li>
<li><p><strong>Extension Methods</strong>: Introduce new methods to existing classes without altering their original code (Scala 3).</p>
</li>
<li><p><strong>Enums</strong>: More advanced than Java enums, capable of containing methods and fields (enhanced in Scala 3).</p>
</li>
<li><p><strong>Union Types</strong>: Specify that a value can belong to one of several types (introduced in Scala 3).</p>
</li>
<li><p><strong>Optional Braces</strong>: Enable cleaner code by using indentation instead of braces (introduced in Scala 3).</p>
</li>
<li><p><strong>Given Instances</strong>: Provide a clearer approach to handling what was previously managed with "implicit" in earlier Scala versions.</p>
</li>
<li><p><strong>Top-level Definitions</strong>: Allow functions and variables to be written without encapsulating them in a class.</p>
</li>
</ul>
<p><strong>Tools and Libraries:</strong></p>
<ul>
<li><p><strong>sbt</strong>: The primary build tool for Scala projects, comparable to Maven or Gradle.</p>
</li>
<li><p><strong>Mill</strong>: A newer and simpler alternative to sbt for building projects.</p>
</li>
<li><p><strong>Metals</strong>: A language server that offers IDE features across various editors.</p>
</li>
<li><p><strong>Scala CLI</strong>: A command-line tool designed for quick Scala scripting and prototyping.</p>
</li>
<li><p><strong>Cats</strong>: A library supporting functional programming with abstractions such as Monads.</p>
</li>
<li><p><strong>ZIO</strong>: A library focused on asynchronous and concurrent programming.</p>
</li>
<li><p><strong>Akka</strong>: A toolkit for developing highly concurrent and distributed applications.</p>
</li>
<li><p><strong>Play Framework</strong>: A web framework akin to Spring Boot.</p>
</li>
<li><p><strong>http4s</strong>: A type-safe, functional HTTP library.</p>
</li>
<li><p><strong>Spark</strong>: A big data processing engine, originally developed as a Scala project.</p>
</li>
<li><p><strong>ScalaTest</strong> and <strong>ScalaCheck</strong>: Libraries designed for testing your code.</p>
</li>
</ul>
<blockquote>
<p>If you're a Java developer wanting to learn something new, this series will show you what makes Scala different and useful. I'll keep everything simple and practical.</p>
<p>The first post will be published this week.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Maximize SDKMAN Utility: Tips for .sdkman Configuration]]></title><description><![CDATA[In the previous article, I described the SDKMAN product. Today, I want to explain how to use the .sdkmanrc file to quickly set up and install the necessary SDKs and toolchains in a project.
Basic sdk env usage
The command sdk env init is used to stor...]]></description><link>https://blog.9mac.dev/maximize-sdkman-utility-tips-for-sdkman-configuration</link><guid isPermaLink="true">https://blog.9mac.dev/maximize-sdkman-utility-tips-for-sdkman-configuration</guid><category><![CDATA[Developer Tools]]></category><category><![CDATA[SDKMAN]]></category><dc:creator><![CDATA[Dawid Świst]]></dc:creator><pubDate>Mon, 28 Apr 2025 19:54:14 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745869914259/e5da501e-701f-42bb-a8f7-26c0adcbd18a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the previous <a target="_blank" href="https://blog.9mac.dev/speed-up-your-java-environment-setup-via-sdkman">article</a>, I described the SDKMAN product. Today, I want to explain how to use the <code>.sdkmanrc</code> file to quickly set up and install the necessary SDKs and toolchains in a project.</p>
<h2 id="heading-basic-sdk-env-usage">Basic sdk env usage</h2>
<p>The command <code>sdk env init</code> is used to store information about the current JDK in the current project (the current directory). This information is stored in the <code>.sdkmanrc</code> file. The following example shows the process of creating such a file.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Go to a project locaton</span>
 <span class="hljs-built_in">cd</span> ~/some-java-project/ 
~/some-java-project/ 

<span class="hljs-comment"># The default version of java in fresh terminal</span>
 java -version
openjdk version <span class="hljs-string">"17.0.11"</span> 2024-04-16 LTS
OpenJDK Runtime Environment Microsoft-9388408 (build 17.0.11+9-LTS)
OpenJDK 64-Bit Server VM Microsoft-9388408 (build 17.0.11+9-LTS, mixed mode, sharing)

<span class="hljs-comment"># setup required version of java</span>
 sdk use java 21.0.6-zulu
Using java version 21.0.6-zulu <span class="hljs-keyword">in</span> this shell.

<span class="hljs-comment"># create .sdkmanrc file</span>
 sdk env init
.sdkmanrc created.

<span class="hljs-comment"># Examining the file's contents.</span>
 cat ./.sdkmanrc
<span class="hljs-comment"># Enable auto-env through the sdkman_auto_env config</span>
<span class="hljs-comment"># Add key=value pairs of SDKs to use below</span>
java=21.0.6-zulu
</code></pre>
<p>When we return to a project location in a new terminal session and run <code>sdk env</code>, the required JDK will be on the path, as shown in the example below:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># default java version</span>
 java -version
openjdk version <span class="hljs-string">"17.0.11"</span> 2024-04-16 LTS
OpenJDK Runtime Environment Microsoft-9388408 (build 17.0.11+9-LTS)
OpenJDK 64-Bit Server VM Microsoft-9388408 (build 17.0.11+9-LTS, mixed mode, sharing)

 <span class="hljs-built_in">cd</span> some-java-project/
~/some-java-project/

<span class="hljs-comment"># change JDK version to required </span>
 sdk env
Using java version 21.0.6-zulu <span class="hljs-keyword">in</span> this shell.
 ~/pharaoh-projects/some-java-project/ java -version
openjdk version <span class="hljs-string">"21.0.6"</span> 2025-01-21 LTS
OpenJDK Runtime Environment Zulu21.40+17-CA (build 21.0.6+7-LTS)
OpenJDK 64-Bit Server VM Zulu21.40+17-CA (build 21.0.6+7-LTS, mixed mode, sharing)

<span class="hljs-comment"># Restore the preview version</span>
 sdk env clear
Restored java version to 17.0.11-ms (default)

 java -version
openjdk version <span class="hljs-string">"17.0.11"</span> 2024-04-16 LTS
OpenJDK Runtime Environment Microsoft-9388408 (build 17.0.11+9-LTS)
OpenJDK 64-Bit Server VM Microsoft-9388408 (build 17.0.11+9-LTS, mixed mode, sharing)
 ~/pharaoh-projects/some-java-project/
</code></pre>
<p>The example also shows how to restore the original version of the JDK using <code>sdk env clear</code>.</p>
<h2 id="heading-store-information-about-additional-tools">Store information about additional tools.</h2>
<p>The file <code>.sdkmanrc</code> and the <code>sdk env</code> command can also be used to store information about additional tools needed for a project.</p>
<p>For example, if we need to use specific versions of Gradle, Ant, and Scala in the project, we can add this information in the <code>.sdkmanrc</code> file in the format <code>&lt;tool_name&gt;=version</code>, as shown below.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># updated version of file with required  tools</span>
 cat ./.sdkmanrc
java=21.0.6-zulu
gradle=8.12
ant=1.10.9
scala=2.13.9
</code></pre>
<p>To install (or be sure) all required tools you have to run <code>sdk env install</code>:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Install tools </span>
sdk env install

java 21.0.6-zulu is already installed.
Downloading: gradle 8.12
In progress...
<span class="hljs-comment">######################################################################## 100.0%#=#=-#  #</span>
Installing: gradle 8.12
Done installing!
Downloading: ant 1.10.9

In progress...
<span class="hljs-comment">######################################################################## 100.0%</span>
Installing: ant 1.10.9
Done installing!

scala 2.13.9 is already installed.
Using java version 21.0.6-zulu <span class="hljs-keyword">in</span> this shell.
Using gradle version 8.12 <span class="hljs-keyword">in</span> this shell.
Using ant version 1.10.9 <span class="hljs-keyword">in</span> this shell.
Using scala version 2.13.9 <span class="hljs-keyword">in</span> this shell.
</code></pre>
<p>The <code>sdk env</code> command will update <code>$PATH</code> for the current session.</p>
<pre><code class="lang-bash"> sdk env
Using java version 21.0.6-zulu <span class="hljs-keyword">in</span> this shell.
Using gradle version 8.12 <span class="hljs-keyword">in</span> this shell.
Using ant version 1.10.9 <span class="hljs-keyword">in</span> this shell.
Using scala version 2.13.9 <span class="hljs-keyword">in</span> this shell.
</code></pre>
<p>When the <code>.sdkmanrc</code> file is stored with the project, it makes it easy to quickly install the necessary development tools when setting up the project in a new environment or on a continuous integration (CI) system.</p>
<p>Example code:</p>
<pre><code class="lang-bash">git <span class="hljs-built_in">clone</span> &lt;my-project&gt;
<span class="hljs-built_in">cd</span> my-project
sdk env install
<span class="hljs-comment"># enviroment is ready</span>
</code></pre>
<blockquote>
<p>The article explains how to use the .sdkmanrc file and SDKMAN to manage SDKs and toolchains for projects. It covers initializing the .sdkmanrc file to store specific JDK versions, using commands like `sdk env` to switch environments, and restoring original versions with `sdk env clear`. Additionally, it demonstrates adding multiple tools such as Gradle, Ant, and Scala to the .sdkmanrc file and how to install these tools with `sdk env install`. This setup is useful for quickly preparing development environments, including in CI systems.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Speed up your Java environment setup via SDKMAN]]></title><description><![CDATA[Intro
Usually, to install a Java Virtual Machine (JVM) and tools related to the Java language, you have to download a package from the internet, open it, and set some environment variables yourself. This can take a lot of time and be a hassle, especi...]]></description><link>https://blog.9mac.dev/speed-up-your-java-environment-setup-via-sdkman</link><guid isPermaLink="true">https://blog.9mac.dev/speed-up-your-java-environment-setup-via-sdkman</guid><category><![CDATA[tools]]></category><category><![CDATA[SDKMAN]]></category><category><![CDATA[sdks for developers]]></category><category><![CDATA[Tools for Developers]]></category><category><![CDATA[java_tools]]></category><dc:creator><![CDATA[Dawid Świst]]></dc:creator><pubDate>Sun, 27 Apr 2025 18:30:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745778416513/f87def74-738d-4665-98b1-2fb73e0656eb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-intro">Intro</h3>
<p>Usually, to install a <strong>Java Virtual Machine (JVM)</strong> and tools related to the Java language, you have to download a package from the internet, open it, and set some environment variables yourself. This can take a lot of time and be a hassle, especially if you need to change JVM versions or set up a new development environment. While package systems like <strong>brew</strong>, <strong>deb</strong>, or <strong>rpm</strong> might seem to make things easier, they usually only let you have one version of the package at a time, making it hard to switch between versions or manage multiple ones. This is where <strong>SDKMAN</strong> becomes very useful, providing a neat and easy way to solve these problems.</p>
<h3 id="heading-how-to-install-sdkman">How to install SDKMAN</h3>
<p>Installing SDKMAN on your system is simple, regardless of your operating system. SDKMAN is a useful tool that simplifies managing software development kits (SDKs), especially for Java developers. Follow these steps to install SDKMAN on Linux, FreeBSD, macOS, and Windows.</p>
<h4 id="heading-for-linux-freebsd-and-macos">For Linux, FreeBSD, and MacOS:</h4>
<ol>
<li><p><strong>Open Your Terminal</strong>: Start by opening a terminal window on your system.</p>
</li>
<li><p><strong>Install SDKMAN</strong>: Execute the following command in your terminal:</p>
<pre><code class="lang-shell"> curl -s "https://get.sdkman.io" | bash
</code></pre>
<p> This command downloads and runs the SDKMAN installation script.</p>
</li>
<li><p><strong>Initialize SDKMAN</strong>: After installation, you'll need to initialize SDKMAN. You can do this by running:</p>
<pre><code class="lang-shell"> source "$HOME/.sdkman/bin/sdkman-init.sh"
</code></pre>
<p> This command ensures that SDKMAN is loaded into your current shell session.</p>
</li>
<li><p><strong>Verify Installation</strong>: To confirm that SDKMAN is correctly installed, you can use the command:</p>
<pre><code class="lang-shell"> sdk version
</code></pre>
<p> If the installation is successful, you should see the version number of SDKMAN displayed.</p>
</li>
</ol>
<h4 id="heading-for-windows">For Windows:</h4>
<p>Windows users can also benefit from SDKMAN by utilizing a Bash emulation environment like Git Bash, Cygwin, or Windows Subsystem for Linux (WSL).</p>
<ol>
<li><p><strong>Install a Bash Emulation Environment</strong>: If you haven't already, install your preferred Bash emulation environment. Windows Subsystem for Linux (WSL) is a popular choice for a more Linux-like experience on Windows.</p>
</li>
<li><p><strong>Follow the Linux Installation Steps</strong>: Once your Bash emulation environment is set up, follow the same installation steps as outlined for Linux, FreeBSD, and MacOS.</p>
</li>
</ol>
<p>By following these instructions, you can efficiently install SDKMAN on your system, regardless of your operating system. SDKMAN significantly simplifies the process of managing and switching between different versions of Java tools, making it a must-have utility for developers.</p>
<h3 id="heading-install-your-your-favourite-java-tools">Install your your favourite Java tools</h3>
<p>To use SDKMAN for managing Java versions, follow these steps for installing a Java version, setting a default version, and switching between JDK versions:</p>
<ol>
<li><p><strong>Install a Specific Java Version</strong>:</p>
<ul>
<li><p>Open your terminal.</p>
</li>
<li><p>To install a specific version of Java, use the command:</p>
<pre><code class="lang-bash">  sdk install java &lt;version&gt;
</code></pre>
<p>  Replace <code>&lt;version&gt;</code> with the version identifier you wish to install. You can find available versions by running <code>sdk list java</code>.</p>
</li>
</ul>
</li>
<li><p><strong>Set a Default Java Version</strong>:</p>
<ul>
<li><p>To set a certain Java version as your default, ensuring it's used in every new terminal session, use the command:</p>
<pre><code class="lang-bash">  sdk default java &lt;version&gt;
</code></pre>
<p>  Again, replace <code>&lt;version&gt;</code> with the version identifier of the Java version you wish to set as default.</p>
</li>
</ul>
</li>
<li><p><strong>Switch Between JDK Versions for the Current Session</strong>:</p>
<ul>
<li><p>If you need to switch to a different Java version for the current terminal session only, use:</p>
<pre><code class="lang-bash">  sdk use java &lt;version&gt;
</code></pre>
<p>  This change will only apply to your current terminal session and will revert to the default version once the session is closed.</p>
</li>
</ul>
</li>
</ol>
<p>By following these steps, you can easily install, manage, and switch between different Java versions using SDKMAN, making it a highly flexible tool for Java development environments.</p>
<p>Certainly! Here are working examples of the commands mentioned:</p>
<ol>
<li><p><strong>Install a Specific Java Version</strong>:</p>
<pre><code class="lang-bash"> sdk install java 11.0.2-open
</code></pre>
<p> This command installs the OpenJDK version 11.0.2.</p>
</li>
<li><p><strong>Set a Default Java Version</strong>:</p>
<pre><code class="lang-bash"> sdk default java 11.0.2-open
</code></pre>
<p> This sets Java version 11.0.2 as the default version for any new terminal session.</p>
</li>
<li><p><strong>Switch Between JDK Versions for the Current Session</strong>:</p>
<pre><code class="lang-bash"> sdk use java 8.0.292-open
</code></pre>
<p> This switches the Java version to OpenJDK 8.0.292 for the current terminal session only.</p>
</li>
</ol>
<h3 id="heading-install-other-tools-with-sdkman">Install Other Tools with SDKMAN</h3>
<p>SDKMAN is not just for managing Java versions; it also lets you install and manage various other tools, including build tools, programming languages, frameworks, and software development utilities. This makes it a versatile tool for developers working in the Java ecosystem and beyond. Off corse the tool allows to switch between different versions of these tools.</p>
<h4 id="heading-examples-of-tools-you-can-install-with-sdkman">Examples of Tools You Can Install with SDKMAN</h4>
<ol>
<li><p><strong>Build Tools</strong>:</p>
<ul>
<li><p><strong>Maven</strong>: A popular build automation tool used primarily for Java projects.</p>
</li>
<li><p><strong>Gradle</strong>: A flexible build automation system that supports multi-language development.</p>
</li>
<li><p><strong>SBT</strong>: The interactive build tool for Scala and Java projects.</p>
</li>
</ul>
</li>
<li><p><strong>Programming Languages</strong>:</p>
<ul>
<li><p><strong>Scala</strong>: A language that combines object-oriented and functional programming.</p>
</li>
<li><p><strong>Groovy</strong>: A powerful, optionally typed and dynamic language, with static-typing and static compilation capabilities.</p>
</li>
</ul>
</li>
<li><p><strong>Frameworks</strong>:</p>
<ul>
<li><p><strong>Spring Boot</strong>: A framework that simplifies the development of new Spring applications.</p>
</li>
<li><p><strong>Micronaut</strong>: A modern, JVM-based, full-stack framework for building modular, easily testable microservice applications.</p>
</li>
</ul>
</li>
<li><p><strong>Software Development Utilities</strong>:</p>
<ul>
<li><p><strong>JBake</strong>: A static site/blog generator for developers.</p>
</li>
<li><p><strong>Jetty</strong>: A Java HTTP (Web) server and Java Servlet container.</p>
</li>
<li><p><strong>Hadoop</strong>: A framework that allows for the distributed processing of large data sets across clusters of computers.</p>
</li>
</ul>
</li>
</ol>
<h4 id="heading-installation-examples">Installation Examples</h4>
<p>Here are some examples of how to install these tools using SDKMAN:</p>
<ol>
<li><p><strong>Install Scala</strong>:</p>
<pre><code class="lang-bash"> sdk install scala 3.6.3
 <span class="hljs-comment"># or </span>
 sdk install scala 3.6.3
</code></pre>
</li>
<li><p><strong>Install Maven</strong>:</p>
<pre><code class="lang-bash"> sdk install maven
</code></pre>
</li>
<li><p><strong>Install Gradle</strong>:</p>
<pre><code class="lang-bash"> sdk install gradle
</code></pre>
</li>
<li><p><strong>Install Spring Boot</strong>:</p>
<pre><code class="lang-bash"> sdk install springboot
</code></pre>
</li>
</ol>
<p>List of available packages you can find <a target="_blank" href="https://sdkman.io/jdks">JDK</a>, <a target="_blank" href="https://sdkman.io/sdks">SDK and tools</a> .</p>
<h3 id="heading-save-your-workshop">Save your workshop</h3>
<p>I understand that changing a Java version or a tool version on the fly can be very useful but not always practical. If you need to switch between different versions and can't always remember which version to use for a project, the SDK provides a feature called <code>sdk env</code>.</p>
<p>The <code>sdk env</code> function in SDKMAN allows for automatic switching of SDK versions based on the project you are working on. It lets you define specific tool versions in a <code>.sdkmanrc</code> configuration file, making it easier to manage environments across different projects. This way, when you enter a project's directory, SDKMAN automatically sets the right tool versions, improving work efficiency.</p>
<p>Let's try something in practice. Let's assume you need to use JDK version 21 Zulu.</p>
<ol>
<li><p>First, navigate to your project directory and install SDKMAN, Java 21.0.6-zulu, and Gradle 8.12.1:</p>
<pre><code class="lang-bash"> sdk install java 21.0.6-zulu
 <span class="hljs-comment"># Downloading: java 21.0.6-zulu</span>
 <span class="hljs-comment"># In progress...</span>
</code></pre>
</li>
<li><p>Next, use the <code>use</code> command to set these versions for the current session:</p>
<pre><code class="lang-bash"> sdk use java 21.0.6-zulu
</code></pre>
</li>
<li><p>Then, utilize <code>sdk init</code> to save this configuration in the <code>.sdkmanrc</code> file:</p>
<pre><code class="lang-bash"> sdk env init
 <span class="hljs-comment"># .sdkmanrc created.</span>
 cat ./.sdkmanrc
 java=21.0.6-zulu
</code></pre>
</li>
</ol>
<p>When you return to the project directory next time, SDKMAN and run sdk env install the tick will automatically reads the <code>.sdkmanrc</code> file and use the specified version. If the required packages are not available SDK will install them</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> &lt;your project dir&gt;
sdk env install
<span class="hljs-comment">#java 21.0.6-zulu is already installed.</span>
<span class="hljs-comment">#Using java version 21.0.6-zulu in this shell.</span>
java -version
<span class="hljs-comment"># openjdk version "21.0.6" 2025-01-21 LTS</span>
<span class="hljs-comment"># OpenJDK Runtime Environment Zulu21.40+17-CA (build 21.0.6+7-LTS)</span>
<span class="hljs-comment"># OpenJDK 64-Bit Server VM Zulu21.40+17-CA (build 21.0.6+7-LTS, mixed mode, sharing)</span>
</code></pre>
<h3 id="heading-useful-links">Useful Links</h3>
<ul>
<li><p><strong>SDKMAN project page</strong> <a target="_blank" href="https://sdkman.io">https://sdkman.io</a></p>
</li>
<li><p><a target="_blank" href="https://sdkman.io/usage">SDKMAN! Documentation</a>.</p>
</li>
<li><p>List of Supported <a target="_blank" href="https://sdkman.io/jdks">JDK Distributions</a></p>
</li>
<li><p>List of supported <a target="_blank" href="https://sdkman.io/sdks">additional SDKs and tools</a></p>
</li>
<li><p><a target="_blank" href="https://sdkman.io/install">Installation manula</a></p>
</li>
</ul>
<h3 id="heading-summary">Summary</h3>
<blockquote>
<p>SDKMAN simplifies managing multiple Java versions and other software development kits, making it an essential tool for developers. It allows effortless installation, version switching, and configuration across Linux, macOS, Windows, and more through a terminal. With SDKMAN, you can install specific Java versions, set default versions, and switch between them as needed. It also supports various tools beyond Java, including build automation tools, additional programming languages, and frameworks. SDKMAN's `sdk env` feature enhances productivity by automatically adjusting tool versions per project directory with `.sdkmanrc` files.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Unveiling the Spring Framework 6: Read the New Series]]></title><description><![CDATA[Hello,
I am pleased to announce a new series on the blog titled “spring-framework-6” Having recently obtained the book “Pro Spring 6”, which covers Spring Framework (version 6.x) and Spring Boot (version 3.x), I am eager to enhance my understanding a...]]></description><link>https://blog.9mac.dev/unveiling-the-spring-framework-6-read-the-new-series</link><guid isPermaLink="true">https://blog.9mac.dev/unveiling-the-spring-framework-6-read-the-new-series</guid><category><![CDATA[Java]]></category><category><![CDATA[Springboot]]></category><category><![CDATA[Spring framework]]></category><dc:creator><![CDATA[Dawid Świst]]></dc:creator><pubDate>Fri, 25 Apr 2025 20:08:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745611572469/d1433ed9-cc11-42b0-962f-f4aa8d64580e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello,</p>
<p>I am pleased to announce a new series on the blog titled “<strong>spring-framework-6</strong>” Having recently obtained the book <a target="_blank" href="https://www.amazon.com/Pro-Spring-Depth-Guide-Framework/dp/1484286391/ref=sr_1_1?crid=3RZLZVKFCPT0&amp;dib=eyJ2IjoiMSJ9.U9Qf4sHT1WE7u7kcxkUNIbGqAr8Y-iT1l1dHZ741VZzjFeA4-FMgQH_O_3yC9w7qQCP2SsnobOHNHBr9sW70dsX8QNK3RyAXaXzrYXwRjE15WODlYpvyj9sI3b5SKz9BT99h0pignkNGR22HIpuC-vwlNhTbl0PT5E1M9Qjrhx3GYBR3L8ZL9jsNthLqgiKubuQl_9cBg33D34HUpeO-hyT1bQNxc2LK2FMlHZaw9WM.B1CggbI_VHDZsAI96UDIYg9deWVyI3K-wqcnFDRgmeM&amp;dib_tag=se&amp;keywords=pro+spring+6&amp;qid=1728213228&amp;sprefix=pro+spring+6%2Caps%2C186&amp;sr=8-1">“Pro Spring 6”</a>, which covers Spring Framework (version 6.x) and Spring Boot (version 3.x), I am eager to enhance my understanding and explore this powerful tool in greater depth. This series will examine solutions and techniques within the Spring Framework. It will include advanced examples of utilizing features and APIs of Spring Framework (version 6.x), complete with runnable examples and code snippets. You can anticipate new articles twice a week. Please note that my intention is not to endorse any book or product but to reference a source of knowledge and inspiration for these articles.</p>
<p>Join me soon to explore Spring Framework 6.</p>
]]></content:encoded></item><item><title><![CDATA["Welcome to the Blog! Explore the Ins and Outs of Programming on the JVM with Java, Scala, and Beyond!"]]></title><description><![CDATA[Welcome to my blog about programming on the JVM platform!
I'm here to share knowledge and experience with programming in Java and Scala. Together, we'll uncover the secrets of these languages and explore topics related to tools, frameworks, and techn...]]></description><link>https://blog.9mac.dev/welcome-to-the-blog-explore-the-ins-and-outs-of-programming-on-the-jvm-with-java-scala-and-beyond</link><guid isPermaLink="true">https://blog.9mac.dev/welcome-to-the-blog-explore-the-ins-and-outs-of-programming-on-the-jvm-with-java-scala-and-beyond</guid><category><![CDATA[hello]]></category><dc:creator><![CDATA[Dawid Świst]]></dc:creator><pubDate>Tue, 22 Apr 2025 18:36:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/3SIXZisims4/upload/ab5e05443d154a36824da349679cb6bc.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Welcome to my blog about programming on the JVM platform!</p>
<p>I'm here to share knowledge and experience with programming in Java and Scala. Together, we'll uncover the secrets of these languages and explore topics related to tools, frameworks, and technologies that will help you create efficient and innovative solutions.</p>
<p>But that's not all! Occasionally, we'll also dive into the world of programming on the Apple platform, focusing on macOS and iOS, as well as the Swift language. I encourage you to explore these areas and broaden your knowledge.</p>
<p>It's crucial to consider cloud technologies and Linux/Unix systems. We'll explore topics related to these areas and learn how to apply them in our daily programming tasks.</p>
<p>Get ready for an amazing journey into the world of programming on the JVM, Apple, and beyond! I'll be here to share helpful tips and spark your inspiration. Let's explore and learn together!</p>
<p>Join me on this exciting journey and enhance your programming skills!</p>
<p>Dawid Świst</p>
]]></content:encoded></item></channel></rss>