Skip to content

Java 14

Neelu edited this page Jul 26, 2026 · 1 revision

☕ Java 14: Records Arrive & Helpful NullPointerExceptions

Release Date: March 17, 2020 | Type: Standard (non-LTS) | Status: ⚠️ End of Life


📋 Table of Contents

  1. Overview
  2. Core Features
  3. Best Practices

🎯 Overview

Java 14 standardizes switch expressions, previews Records (the biggest boilerplate-killer since lambdas) and pattern matching for instanceof, and ships one of the most beloved diagnostic improvements ever: NullPointerExceptions that actually tell you what was null.

mindmap
  root((Java 14))
    Switch Expressions
      Standardized JEP 361
    Records
      Preview JEP 359
    Pattern Matching instanceof
      Preview JEP 305
    Helpful NPEs
      JEP 358
Loading

🚀 Core Features

1️⃣ Switch Expressions (Standard)

// Now a permanent language feature — no --enable-preview needed
String typeDescription = switch (obj) {
    case Integer i -> "int: " + i;      // (pattern matching combo comes later, in 21)
    default -> "unknown";
};

int numLetters = switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> 6;
    case TUESDAY -> 7;
    case THURSDAY, SATURDAY -> 8;
    case WEDNESDAY -> 9;
};

2️⃣ Records (Preview)

What: A compact syntax for classes that are just immutable data carriers — the compiler generates the constructor, accessors, equals(), hashCode(), and toString() for you.

// Before: ~30 lines of boilerplate
public final class Point {
    private final int x;
    private final int y;
    public Point(int x, int y) { this.x = x; this.y = y; }
    public int x() { return x; }
    public int y() { return y; }
    @Override public boolean equals(Object o) { /* ... */ }
    @Override public int hashCode() { /* ... */ }
    @Override public String toString() { /* ... */ }
}

// Java 14 record — one line, same guarantees
public record Point(int x, int y) {}

// Usage
Point p = new Point(3, 4);
p.x();          // 3
p.y();          // 4
p.toString();   // "Point[x=3, y=4]"
p.equals(new Point(3, 4)); // true — structural equality, generated for you
classDiagram
    class Point {
        -int x
        -int y
        +Point(int x, int y)
        +x() int
        +y() int
        +equals(Object) boolean
        +hashCode() int
        +toString() String
    }
    note for Point "All of this generated\nfrom: record Point(int x, int y) {}"
Loading

Records can have custom logic too:

public record Range(int start, int end) {
    // Compact canonical constructor — runs before field assignment
    public Range {
        if (start > end) {
            throw new IllegalArgumentException("start must be <= end");
        }
    }

    // Extra derived methods are allowed
    public int length() {
        return end - start;
    }
}

3️⃣ Pattern Matching for instanceof (Preview)

// Before: cast after every instanceof check
if (obj instanceof String) {
    String s = (String) obj;
    System.out.println(s.length());
}

// Java 14 preview: pattern variable binds automatically
if (obj instanceof String s) {
    System.out.println(s.length());   // s is already a String, no cast needed
}

// The pattern variable is even usable in surrounding boolean logic
if (obj instanceof String s && s.length() > 5) {
    System.out.println("Long string: " + s);
}

4️⃣ Helpful NullPointerExceptions

What: -XX:+ShowCodeDetailsInExceptionMessages (default-on since Java 15) tells you exactly which variable was null.

// Before Java 14
// Exception in thread "main" java.lang.NullPointerException
//     at Main.main(Main.java:8)

// Java 14+
order.getCustomer().getAddress().getCity();
// Exception in thread "main" java.lang.NullPointerException:
//   Cannot invoke "Address.getCity()" because the return value of
//   "Customer.getAddress()" is null
# Enable explicitly on Java 14 (on by default from Java 15+)
java -XX:+ShowCodeDetailsInExceptionMessages -jar app.jar

Why it matters: This single feature has saved countless debugging hours on long method-chain calls (a.getB().getC().getD()) where the stack trace alone couldn't tell you which link in the chain was null.


5️⃣ NVM (Non-Volatile Memory) Support

Low-level JEP allowing memory-mapped byte buffers to target NVM devices — mostly relevant to specialized high-performance storage systems, not typical application code.


💡 Best Practices

✅ Reach for record for DTOs, value objects, and API request/response bodies — huge boilerplate reduction
✅ Combine instanceof pattern matching with && conditions for concise guard clauses
✅ Rely on helpful NPE messages during debugging — don't manually add null checks just to identify the null variable
❌ Don't use records for entities that need mutability or identity semantics (e.g., JPA entities) — records are inherently immutable and final

// Records + pattern matching is a preview of what's coming in Java 21's record patterns
record Point(int x, int y) {}

static String describe(Object obj) {
    if (obj instanceof Point p && p.x() == p.y()) {
        return "Point on the diagonal";
    }
    return "Something else";
}

Clone this wiki locally