Chapter 14 of 16Foundation

Modern Java

Write concise code with lambdas and streams, then apply records, sealed types, and pattern switches deliberately.

30 minJava 21
01

Why it matters

Start from the concept, not the syntax

Meet a concrete situation where this idea earns its place, then state the boundary it guards.

Modern Java is noticeably shorter than the Java of a decade ago. Lambdas compress behavior into expressions, streams declare transformations instead of loops, records collapse data classes to one line, and sealed types plus pattern switches make exhaustive checking compiler-enforced.

By the end of this chapter

Verbose anonymous classes and manual type checks make code longer and more error-prone than it needs to be.

  • Lambdas and streams
  • Records
  • Sealed types and pattern switch
02

Mechanism

How Java expresses the concept

Walk the actual types, rules, and runtime behavior involved—the same behavior described in the Java Language Specification.

The mental model

A functional interface has exactly one abstract method and can be fulfilled by a lambda. `Runnable`, `Comparator`, and `Callable` are functional interfaces; lambdas replace anonymous classes for short behavior. A lambda captures variables only if they are effectively final.

Streams pipeline collections: `stream().filter(...).map(...).collect(...)` expresses what to compute, not how to loop. Use streams for transformation and clarity; fall back to a plain loop when the work has side effects or needs careful ordering, and avoid `parallelStream` unless you have measured a need.

A `record` is a concise, shallowly immutable data carrier: `record Point(int x, int y) {}` generates the fields, a constructor, accessors, `equals`, `hashCode`, and `toString`. Sealed types (`sealed interface`) restrict which classes may implement them. A Java 21 pattern switch can then be exhaustive when its cases cover the permitted type space; `null` and separate-compilation edge cases still require care.

Pattern matching for `instanceof` and the `switch` pattern form remove boilerplate casts and make type-based branching exhaustive and checked by the compiler. These features keep Java expressive without adding a separate runtime.

03

Concept map

Inspect the building blocks as a whole

Use the map as a structural reference; it shows the pieces and their relationships, not an execution sequence.

Core Java

Modern Java: lambdas, streams, and records

Java 8 and later added functional style and compact data types. Records, sealed types, and pattern switches make Java code shorter and safer.

  1. 01Functional interfaces
  2. 02Lambdas capture final
  3. 03Stream pipelines
  4. 04Record data carriers
  5. 05Sealed types
  6. 06Pattern switch
04

Implementation

Encode the smallest runnable example

Write and run the code, then observe the behavior you just predicted with a real Java program.

Runnable snippet path

Predict, run, and change each example

2 focused examples
01

Stream a transformation

filter, map, and collect into a new list without a manual loop.

Names.java
import java.util.*;

public class Names {
    static List<String> upper(List<String> items) {
        return items.stream()
            .filter(s -> !s.isBlank())
            .map(String::toUpperCase)
            .toList();
    }
}

Remember

Use streams for clear transformations; keep side effects out of them.

Change one input, predict the result, then compile or test it before continuing.

02

Record and pattern switch

A record carries data; a sealed type lets the switch be exhaustive and checked.

Shape.java
sealed interface Shape permits Circle, Square {}
record Circle(double r) implements Shape {}
record Square(double s) implements Shape {}

class Area {
    static double area(Shape s) {
        return switch (s) {
            case Circle c -> Math.PI * c.r() * c.r();
            case Square q -> q.s() * q.s();
        };
    }
}

Remember

This pattern switch is exhaustive for the two permitted non-null types; add a deliberate `case null` or default when null is part of the input contract.

Change one input, predict the result, then compile or test it before continuing.

05

Watch out

Break it on purpose, then repair the right layer

Predict the symptom before running the failure case, and close with the completion checklist.

Deliberate failure

  1. Break

    Replace a missing case in a type switch with a silent default branch

  2. Observe

    A newly added subtype is silently handled wrong

  3. Repair

    Use sealed types so the compiler enforces exhaustive handling instead of trusting a catch-all default

Why it matters

Use sealed types so the compiler enforces exhaustive handling instead of trusting a catch-all default

Completion checklist

  • Use lambdas and stream pipelines for transformation
  • Model data with records
  • Branch exhaustively over sealed types with a pattern switch
06

Go deeper

Official references

The primary sources behind this chapter. Every claim above stays consistent with them.