Skip to main content
java interview engineering first principles to system design

Records, Sealed Classes, and Pattern Matching

7 min read Chapter 2 of 32
Summary

This section explores the integration of Java Records,...

This section explores the integration of Java Records, sealed classes, and pattern matching for type-safe interview solutions. Records are immutable data carriers that automatically generate methods like equals and hashCode, reducing boilerplate by up to 90% compared to POJOs; they use compact constructors for validation, as shown in the Point record example. Sealed classes, using the 'sealed' modifier and 'permits' clause, create closed hierarchies like the Expression interface with Add, Multiply, and Constant subtypes, all implemented as Records. Pattern matching in switch expressions enables exhaustive handling of these subtypes, demonstrated in the evaluate method that traverses expression trees with O(n) time complexity. Key comparisons highlight trade-offs: Records vs. POJOs for immutability vs. flexibility, and sealed vs. open hierarchies for type safety vs. extensibility. A failure mode checklist addresses pitfalls like unhandled cases and validation oversights. The section concludes with a structured interview template that guides candidates from problem analysis to implementation using these modern Java features.

Records, Sealed Classes, and Pattern Matching

Building upon the foundational Java 21+ features introduced in the parent chapter, this section delves into the synergistic use of Records, sealed classes, and pattern matching to craft type-safe solutions for technical interviews. These elements are not mere syntax enhancements but essential tools that enforce correctness at compile time, reduce boilerplate, and eliminate runtime errors, equipping candidates with a modern, efficient approach to problem-solving. By mastering these features, readers can implement immutable data carriers, closed type hierarchies, and exhaustive case handling, all while adhering to Java’s latest standards.

Defining Java Records for Immutable Data Carriers

A Java Record, finalized in Java 16 and fully supported in Java 21+, is an immutable data carrier class that automatically generates canonical constructors, accessor methods, equals, hashCode, and toString implementations. This design reduces verbosity by up to 90% compared to traditional POJOs (Plain Old Java Objects), which require manual coding of these elements. Records are inherently final and cannot extend other classes, making them ideal for scenarios where data integrity and thread-safety are paramount, such as in interview problems involving data transfer or modeling simple entities.

Key components of a Record include record components, which are fields that are final by default and accessible via generated methods. For example, the Point record demonstrates a compact constructor for validation, ensuring that coordinates are non-negative while maintaining immutability:

public record Point(int x, int y) {
    public Point {
        if (x < 0 || y < 0) throw new IllegalArgumentException("Coordinates must be non-negative");
    }
}

This code defines a Point with components x and y. The compact constructor omits the parameter list and allows direct validation using the components, preventing invalid state upon instantiation. Records like this provide conciseness and immutability at the cost of mutable state flexibility; they cannot have mutable fields or be subclassed, aligning with interview demands for predictable, error-resistant code.

Implementing Sealed Classes for Closed Hierarchies

Sealed classes in Java 21+ use the sealed modifier and a permits clause to restrict which classes can extend them, creating a closed hierarchy of types. This enables compiler-checked exhaustive handling in pattern matching, which is crucial for modeling fixed sets such as expression nodes or state machines in interview scenarios. A sealed hierarchy ensures that all permitted subtypes are known at compile time, reducing the risk of runtime errors from unhandled cases.

Consider a sealed interface Expression designed for evaluating mathematical expressions, as defined in the existing Shape interface from the parent chapter. This hierarchy uses Records for its subtypes to combine immutability with type safety:

sealed interface Expression permits Add, Multiply, Constant {}
record Add(Expression left, Expression right) implements Expression {}
record Multiply(Expression left, Expression right) implements Expression {}
record Constant(int value) implements Expression {}

static int evaluate(Expression expr) {
    return switch(expr) {
        case Add a -> evaluate(a.left()) + evaluate(a.right());
        case Multiply m -> evaluate(m.left()) * evaluate(m.right());
        case Constant c -> c.value();
    };
}

The permits clause lists Add, Multiply, and Constant as the only allowed implementations, all defined as Records. This setup facilitates exhaustive pattern matching in the evaluate method, where the switch expression handles all permitted subtypes without explicit casting. Sealed classes provide type safety and exhaustive handling at the cost of a fixed hierarchy, which must be explicitly managed through the permits clause.

Applying Pattern Matching for Exhaustive Type Handling

Pattern matching in Java 21+ extends switch expressions to allow type patterns that match and extract data from objects, eliminating the verbosity of instanceof checks and explicit casts. When combined with sealed classes, it enables exhaustive pattern matching, ensuring compile-time safety by requiring all possible cases of a sealed type to be handled. This feature is exemplified in the evaluate method above, where each case corresponds to a subtype of Expression, and the compiler verifies completeness.

The time complexity of such pattern matching operations is critical for interview analysis. For the evaluate method, which traverses an expression tree, the complexity is linear in the number of nodes:

OperationTime ComplexityExplanation
evaluate(Expression)O(n)Where n is the total number of nodes in the expression tree, as each node is visited once.

This table highlights that exhaustive pattern matching does not add overhead beyond the inherent traversal, maintaining efficiency while enhancing type safety. Pattern matching simplifies code by destructuring Records in switch cases, as seen with Add a extracting left() and right() components without manual accessor calls.

Comparing Trade-offs for Interview Scenarios

In technical interviews, selecting between Records and POJOs, or sealed and open hierarchies, involves explicit trade-offs that impact code maintainability and error resistance. The following matrix outlines these comparisons, drawn from practical considerations:

FeatureProsCons
RecordsConcise code, immutability, automatic methodsNo mutable fields, cannot extend other classes
POJOsFlexibility for mutation, can extend classesVerbose with manual getters, setters, equals/hashCode
Sealed ClassesType safety, exhaustive pattern matchingFixed hierarchy, requires permits clause
Open HierarchiesUnlimited extension, flexibleRisk of runtime errors, no compile-time checks

Records provide immutability and conciseness at the cost of mutable state flexibility, making them suitable for data carriers where state changes are not required. In contrast, POJOs offer more control but increase boilerplate and potential for bugs. Sealed classes excel in scenarios with fixed type sets, enabling compile-time checks, while open hierarchies allow extensibility but sacrifice safety. Candidates should weigh these trade-offs based on problem constraints, such as whether the data model is immutable or the type hierarchy is closed.

Identifying Common Failure Modes

When using Records, sealed classes, and pattern matching, several failure modes can undermine type safety and correctness in interview solutions. Awareness of these pitfalls ensures robust implementations:

  • Not handling all cases in pattern matching switch for sealed types, leading to compile-time errors.
  • Forgetting to make Record components serializable when serialization is needed.
  • Using mutable fields in Records, which is not allowed.
  • Not validating input in compact constructors, risking invalid state.
  • Ignoring edge cases like null inputs in pattern matching (though nullability is out of scope per constraints).

This checklist serves as a guide for testing and validation. For instance, in the Point record, omitting validation in the compact constructor could allow negative coordinates, violating business rules. Similarly, in the Expression hierarchy, failing to include all permitted subtypes in the switch expression triggers a compiler error, enforcing completeness. By addressing these failure modes, candidates demonstrate attention to detail and error prevention.

Template for Interview Problem-Solving

A structured template integrates Records, sealed classes, and pattern matching into a repeatable process for technical interviews, ensuring type-safe and efficient solutions:

  1. Understand problem constraints and identify immutable data carriers—use Records.
  2. If type hierarchy is fixed and small, define a sealed interface or class with permits clause.
  3. Implement subtypes as Records or classes, ensuring they are final, sealed, or non-sealed.
  4. Use pattern matching in switch expressions for exhaustive handling of cases.
  5. Test with edge cases: validate inputs, check for null (if applicable), and ensure all types are covered.
  6. Analyze time and space complexity, adhering to style guide rules.

This template emphasizes the node goal: implementing a polymorphic expression evaluator with sealed types and pattern matching in under ten minutes. For example, applying the template to the Expression hierarchy yields the evaluate method shown earlier, with O(n) complexity and compile-time safety. By following this approach, candidates can systematically leverage modern Java features to produce clean, error-resistant code that impresses interviewers.

In summary, Records, sealed classes, and pattern matching form a cohesive toolkit for type-safe interview solutions in Java 21+. Records offer immutable data carriers with minimal boilerplate, sealed classes enable closed hierarchies for exhaustive handling, and pattern matching simplifies type checks and data extraction. By integrating these elements—as demonstrated through code examples, complexity analysis, trade-offs, failure modes, and a problem-solving template—readers gain the proficiency to tackle interview challenges with confidence and precision, ensuring their code is both modern and robust.