Skip to content
CMO & CTO
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

  • Digital Experience
    • Experience Strategy
    • Experience-Driven Commerce
    • Multi-Channel Experience
    • Personalization & Targeting
    • SEO & Performance
    • User Journey & Behavior
  • Marketing Technologies
    • Analytics & Measurement
    • Content Management Systems
    • Customer Data Platforms
    • Digital Asset Management
    • Marketing Automation
    • MarTech Stack & Strategy
    • Technology Buying & ROI
  • Software Engineering
    • Software Engineering
    • Software Architecture
    • General Software
    • Development Practices
    • Productivity & Workflow
    • Code
    • Engineering Management
    • Business of Software
    • Code
    • Digital Transformation
    • Systems Thinking
    • Technical Implementation
  • About
CMO & CTO

Closing the Bridge Between Marketing and Technology, By Luis Fernandez

Java 14 Records and Sealed Classes

Posted on May 6, 2020 By Luis Fernandez

Java 14 just landed on my desk and the two features that keep popping up in code reviews are Records and Sealed Classes. One is already in our hands as a preview and the other is peeking from early builds, and both point to a cleaner way to say what our types mean without ceremony.

Records feel like the language finally admits that data carriers are first class citizens. You declare intent once, and the compiler does the grind. A tiny type, compact and honest, that rolls with immutability by default and gives you the bits you usually write on autopilot. Less boilerplate, more signal.

record Point(int x, int y) {}

class Demo {
    public static void main(String[] args) {
        var p = new Point(10, 20);
        System.out.println(p);           // Point[x=10, y=20]
        System.out.println(p.x());       // 10
        System.out.println(p.y());       // 20

        var q = new Point(10, 20);
        System.out.println(p.equals(q)); // true
        System.out.println(p.hashCode()); 
    }
}

The intent is loud. State is defined in the header, and you get equals, hashCode and toString that match that state. If you need invariants, a compact constructor keeps it tight. No getters with random names, no fields drifting out of sync with equality, no surprise mutability.

record Email(String user, String domain) {
    public Email {
        if (user == null || user.isBlank()) throw new IllegalArgumentException("user");
        if (domain == null || !domain.contains(".")) throw new IllegalArgumentException("domain");
        user = user.toLowerCase();
        domain = domain.toLowerCase();
    }

    public String address() {
        return user + "@" + domain;
    }
}

This reads like a spec. The shape of the data, a couple of guards, and a friendly method. For DTOs, API payloads, configs, and tiny domain nuggets, Records turn chores into a one liner. And yes, they are immutable by default, which nudges you into safer designs for concurrency and caching.

There is also pattern matching for instanceof in the same release, which pairs nicely with Records. No more cast dance right after the check. The variable just appears where you need it, and the flow is obvious.

Object o = new Point(3, 4);

if (o instanceof Point p) {
    System.out.println(p.x() + p.y());
}

Now about Sealed Classes. While they are not in the final bits of this release, they are near, and the idea is too good to ignore. Seal a type and you say who can extend it. No more accidental hierarchies in other modules, no more mystery subclasses surprising your switch or visitor. You decide the family.

public sealed interface Shape permits Circle, Rect {}

public final class Circle implements Shape {
    public final int r;
    public Circle(int r) { this.r = r; }
}

public final class Rect implements Shape {
    public final int w, h;
    public Rect(int w, int h) { this.w = w; this.h = h; }
}

That permits list is the contract. Tooling can warn you when a match is not complete, and readers see the whole space in one place. Combine that with Records for the leaves and you get sum types with a Java feel. Less confusion, more predictable maintenance.

Before this, many of us reached for Lombok or a hand rolled base class to get similar ergonomics. Now the core language moves in that direction with clear syntax and baked guarantees. Lombok still brings a bag of tricks, and there are spots where you want full classes with custom logic, but for the common case Records are a sweet spot.

There are tradeoffs to keep in mind. Records are about state, not behavior oriented hierarchies. You can add methods, sure, though if that behavior grows into a kitchen sink, you probably want a regular class. Serialization also matters for some teams, so check your format choices and versioning story. And since this is a preview, you need to pass the right compiler and runtime flags and you accept that edges may change.

From the perspective of readability, this is a win. Fewer lines with higher meaning beats lots of lines with ceremony. Code reviews speed up because intent is obvious. Bugs hide in clutter, and Records remove a pile of clutter we all tolerated for years.

On the sealed side, I love what it does for API design. When a library exposes a sealed root type, users see the valid shapes from day one. You can evolve the set in controlled releases, and tests can cover the full space without guessing. In big codebases, that kind of clarity pays for itself.

If you want to try this today, grab Java 14, enable preview for your build, and run a small slice of your project with Records. Keep it focused. A model package here, a couple of endpoints there. Measure what it does to your readability and diffs. Then look at your type hierarchies and plan which ones could be sealed once the bits arrive in a release you can ship with confidence.

Short version Records cut noise for data and Sealed Classes keep your shapes honest.

I am keeping them in my toolbox and rolling them into new services where they fit.

Code Development Practices Software Engineering coding-practicesjavaspring

Post navigation

Previous post
Next post
  • Digital Experience (94)
    • Experience Strategy (19)
    • Experience-Driven Commerce (5)
    • Multi-Channel Experience (9)
    • Personalization & Targeting (21)
    • SEO & Performance (10)
  • Marketing Technologies (92)
    • Analytics & Measurement (14)
    • Content Management Systems (45)
    • Customer Data Platforms (4)
    • Digital Asset Management (8)
    • Marketing Automation (6)
    • MarTech Stack & Strategy (10)
    • Technology Buying & ROI (3)
  • Software Engineering (310)
    • Business of Software (20)
    • Code (30)
    • Development Practices (52)
    • Digital Transformation (21)
    • Engineering Management (25)
    • General Software (82)
    • Productivity & Workflow (30)
    • Software Architecture (85)
    • Technical Implementation (23)
  • 2025 (12)
  • 2024 (8)
  • 2023 (18)
  • 2022 (13)
  • 2021 (3)
  • 2020 (8)
  • 2019 (8)
  • 2018 (23)
  • 2017 (17)
  • 2016 (40)
  • 2015 (37)
  • 2014 (25)
  • 2013 (28)
  • 2012 (24)
  • 2011 (30)
  • 2010 (42)
  • 2009 (25)
  • 2008 (13)
  • 2007 (33)
  • 2006 (26)

Ab Testing Adobe Adobe Analytics Adobe Target AEM agile-methodologies Analytics architecture-patterns CDP CMS coding-practices content-marketing Content Supply Chain Conversion Optimization Core Web Vitals customer-education Customer Data Platform Customer Experience Customer Journey DAM Data Layer Data Unification documentation DXP Individualization java Martech metrics mobile-development Mobile First Multichannel Omnichannel Personalization product-strategy project-management Responsive Design Search Engine Optimization Segmentation seo spring Targeting Tracking user-experience User Journey web-development

©2025 CMO & CTO | WordPress Theme by SuperbThemes