Checkpoint: Modern Java

Reviewed & published by Brayan K

This checkpoint consolidates the modern Java unit — var, text blocks, switch expressions, pattern matching, sealed types, record patterns, method references, and functional interfaces — into one build challenge you complete end to end.

Part of the free Java course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

How This Checkpoint Works

First skim the recap, then tackle the multi-step build challenge on your own. A full, compiled solution is hidden under "Show solution" — try before you peek. Finish with the written checkpoint quiz and the timed quiz to confirm it all stuck.

Unit Recap — Eight Features

Quick Recap in Code

A single program touching several of the features at once: var, a Predicate + method reference, a switch expression, a text block, and an instanceof pattern.

import java.util.List;
import java.util.function.Predicate;

public class Main {
    // Recap in one place: var, text block, switch expression,
    // pattern matching, functional interface, method reference.
    public static void main(String[] args) {
        var items = List.of("Java", "", "Streams", "  ", "Records");

        // Predicate + method reference (Predicate.not + String::isBlank).
        Predicate<String> meaningful = Predicate.not(String::isBlank);
        long kept = items.stream().filter(meaningful).count();

        // switch expression returning a value.
        String verdict = switch ((int) kept) {
            case 0 -> "nothing";
            case 1 -> "one item";
            default -> kept + " items";
        };

        // text block summary.
        String report = """
                Filtered report
                ---------------
                Kept: %s""".formatted(verdict);
        System.out.println(report);

        // pattern matching for instanceof on a mixed value.
        Object value = 42;
        if (value instanceof Integer i && i > 0) {
            System.out.println("positive int: " + i);
        }
    }
}

🏗️ Build Challenge — A Shape Report

Model a small sealed hierarchy of shape records, then process it with a pattern switch that deconstructs each record — wiring in var, a text block, a guard, method references, and streams. Follow the five steps in the starter, aim for the exact expected output, and only then open the solution.

import java.util.List;

public class Main {
    // BUILD CHALLENGE — model shapes and process them with modern Java.
    //
    // STEP 1: a sealed interface Shape permits Circle, Rectangle, Triangle.
    // STEP 2: each shape is a record:
    //           Circle(double radius), Rectangle(double width, double height),
    //           Triangle(double base, double height).
    // STEP 3: write area(Shape) as an EXHAUSTIVE pattern switch that
    //           DECONSTRUCTS each record (no default):
    //             Circle(r)        -> PI * r * r
    //             Rectangle(w, h)  -> w * h
    //             Triangle(b, h)   -> 0.5 * b * h
    // STEP 4: write describe(Shape) returning a text like
    //           "Circle: area=12.57" using "...".formatted(...).
    //           Add a GUARD so a Rectangle with w == h is called "Square".
    // STEP 5: in main, use 'var' + List.of for the shapes, print a text-block
    //           header, then a method-reference forEach over describe, and a
    //           stream sum of area for the total.
    //
    // Expected output:
    //   === Shape Report ===
    //   Circle: area=12.57
    //   Rectangle: area=12.00
    //   Square: area=25.00
    //   Triangle: area=6.00
    //   Total area = 55.57

    public static void main(String[] args) {
        // var shapes = List.of(new Circle(2), new Rectangle(3, 4),
        //                      new Rectangle(5, 5), new Triangle(6, 2));
        // ...
    }
}

✅ Full Solution

Attempt the challenge first. When you're ready, expand the compiled, runnable solution below.

import java.util.List;

public class Main {
    // A sealed hierarchy of shapes, each a record (algebraic data type).
    sealed interface Shape permits Circle, Rectangle, Triangle {}
    record Circle(double radius) implements Shape {}
    record Rectangle(double width, double height) implements Shape {}
    record Triangle(double base, double height) implements Shape {}

    // Process the hierarchy with a pattern switch + record deconstruction.
    // Exhaustive over the sealed type -> no default needed.
    static String describe(Shape s) {
        return switch (s) {
            case Circle(double r) ->
                "Circle: area=%.2f".formatted(Math.PI * r * r);
            case Rectangle(double w, double h) when w == h ->
                "Square: area=%.2f".formatted(w * h);
            case Rectangle(double w, double h) ->
                "Rectangle: area=%.2f".formatted(w * h);
            case Triangle(double b, double h) ->
                "Triangle: area=%.2f".formatted(0.5 * b * h);
        };
    }

    // A second exhaustive switch reusing the record patterns.
    static double area(Shape s) {
        return switch (s) {
            case Circle(double r) -> Math.PI * r * r;
            case Rectangle(double w, double h) -> w * h;
            case Triangle(double b, double h) -> 0.5 * b * h;
        };
    }

    public static void main(String[] args) {
        // 'var' + List.of, text block header, method-reference forEach.
        var shapes = List.of(
            new Circle(2),
            new Rectangle(3, 4),
            new Rectangle(5, 5),
            new Triangle(6, 2)
        );

        String header = """
                === Shape Report ===
                """;
        System.out.print(header);

        shapes.stream()
              .map(Main::describe)
              .forEach(System.out::println);

        double total = shapes.stream()
                             .mapToDouble(Main::area)
                             .sum();
        System.out.printf("Total area = %.2f%n", total);
    }
}
=== Shape Report ===
Circle: area=12.57
Rectangle: area=12.00
Square: area=25.00
Triangle: area=6.00
Total area = 55.57

Notice the design: a sealed Shape of three records; describe and area are both exhaustive pattern switches with no default; the when w == h guard splits squares from rectangles; and main uses var, a text block, Main::describe as a method reference, and a stream sum.

📝 Checkpoint Quiz

Answer each in your head, then reveal.

Answer: Shape is sealed and permits exactly Circle, Rectangle, and Triangle. With an arm for each, the compiler proves the switch is exhaustive, so a default is unnecessary — and omitting it means adding a fourth shape would turn the switch into a compile error until you handle it.

Answer: It is a record pattern. It matches a Circle and immediately deconstructs it, binding the component to r — so you use r directly instead of calling c.radius().

Answer: A switch tries arms top to bottom and takes the first match. The plain case Rectangle(double w, double h) matches every rectangle, so the when w == h arm must precede it — otherwise the guarded arm would be unreachable, which is a compile error.

Answer: var shapes = List.of(...) is fine — a local with an initializer. var could not appear on the record components, the method parameters (Shape s), or the return types of area/describe, because those are part of the API contract.

Answer: Type::staticMethod — describe is a static method of Main. In .map(Main::describe) it stands in for s -> Main.describe(s).

Answer: "Circle: area=%.2f".formatted(value) is the instance form of String.format; %.2f inserts the area rounded to two decimals. There is no string interpolation in Java, so placeholders plus .formatted are the idiom.

Watch Out For

Carry These Forward

📋 Quick Reference — Modern Java at a Glance

FeatureSyntaxSince
varvar x = expr;Java 10
Text block"""\n...\n"""Java 15
Switch expressionvar v = switch (k) {...};Java 14
yieldcase X -> { yield v; }Java 14
instanceof patterno instanceof String sJava 16
sealed / permitssealed ... permits A, BJava 17
switch patternscase Integer i -> ...Java 21
Guardcase X x when cond -> ...Java 21
Record patterncase Point(int x, int y)Java 21
Method referenceType::method, Type::newJava 8
Functional interfaceFunction, Predicate, ...Java 8
.formatted()"%s".formatted(x)Java 15

📚 Resources / Keep Learning

🎉 Checkpoint Cleared!

You combined var, text blocks, switch expressions, pattern matching, sealed types, record patterns, method references, and functional interfaces into a single working program — the heart of modern, data-oriented Java.

Practice quiz

Where can var be used?

  • Fields and parameters
  • Return types
  • Only local variables with an initializer
  • Anywhere

Answer: Only local variables with an initializer. var is for local variables with an initializer — never fields, parameters, or return types.

How do you insert values into a text block?

  • .formatted(...) or String.format
  • ${var} interpolation
  • + only
  • You can't

Answer: .formatted(...) or String.format. Java has no interpolation; use %s/%d placeholders with .formatted(...).

What does a switch expression produce?

  • Nothing
  • Only side effects
  • A boolean
  • A value you can assign or return

Answer: A value you can assign or return. A switch expression evaluates to a value, using arrow arms and yield for blocks.

What does if (o instanceof String s) give you?

  • A boolean only
  • A test plus a ready-to-use String s
  • A cast exception
  • A new string

Answer: A test plus a ready-to-use String s. Pattern matching for instanceof tests the type and binds s, no explicit cast.

Why can a switch over a sealed type omit default?

  • permits lists all subtypes, so the compiler proves exhaustiveness
  • Switches never need default
  • Sealed disables default
  • It uses reflection

Answer: permits lists all subtypes, so the compiler proves exhaustiveness. Because the set of subtypes is closed and known, an arm per subtype is exhaustive.

What does case Point(int x, int y) do?

  • Creates a Point
  • Compares points
  • Matches a Point and deconstructs it into x and y
  • Calls a method

Answer: Matches a Point and deconstructs it into x and y. A record pattern matches and binds the components in one step.

Which is a constructor reference?

  • Integer::parseInt
  • ArrayList::new
  • System.out::println
  • String::length

Answer: ArrayList::new. Type::new is a constructor reference; the others are static, bound, and unbound instance references.

Which functional interface takes a T and returns a boolean?

  • Function<T,R>
  • Consumer<T>
  • Supplier<T>
  • Predicate<T>

Answer: Predicate<T>. Predicate<T>.test returns boolean.

f.andThen(g).apply(x) computes:

  • f(g(x))
  • g(f(x))
  • f(x) and g(x)
  • Nothing

Answer: g(f(x)). andThen runs f first, then feeds the result to g: g(f(x)).

Which combination enables data-oriented programming in modern Java?

  • var + loops
  • threads + locks
  • sealed types + records + pattern matching
  • reflection + generics

Answer: sealed types + records + pattern matching. Sealed types model a closed set, records hold the data, and patterns process them exhaustively.

Continue this course

Frequently asked questions

What did this Modern Java unit cover?

It walked through the features that define Java from version 10 to 21: var for local-variable type inference; text blocks for multi-line strings; switch expressions that produce a value; pattern matching for instanceof and switch; sealed classes and interfaces for closed hierarchies; record patterns for deconstruction; method references as a compact lambda shorthand; and the built-in functional interfaces (Function, Predicate, Consumer, Supplier and friends). Together they enable a clear, data-oriented style.

How do sealed types, records, and pattern matching fit together?

They are three parts of one idea. A sealed interface declares a closed set of cases. Records define each case as immutable data with named components. A pattern switch then dispatches over those cases — deconstructing each record inline — and because the set is closed, the compiler proves the switch is exhaustive. The payoff is that adding a new case turns every incomplete switch into a compile error, so the compiler guides you to every place that must change.

When should I reach for var versus an explicit type?

Use var when the right-hand side already states the type clearly — new ArrayList<String>(), a constructor, or an obvious literal — so it just removes redundant noise. Prefer an explicit type when the right-hand side is an opaque method call whose result type a reader couldn't otherwise see. And remember var is locals-only: it never appears on fields, parameters, or return types, which still document your API.

Do these features require the latest Java?

They were finalized across several releases: var in Java 10, switch expressions in 14, text blocks in 15, instanceof patterns in 16, sealed types in 17, and switch patterns plus record patterns in 21. Method references and functional interfaces date back to Java 8. To use everything in this unit together — especially record patterns — compile and run on Java 21 or newer, which is the current long-term-support release.

What comes after this checkpoint?

The next lesson dives into Stream Collectors — the Collectors utility class that turns a stream into grouped maps, joined strings, partitions, and summary statistics. Collectors pair naturally with the functional interfaces and method references you just practised, so this checkpoint is the ideal springboard into deeper stream processing.