DEV Community

9mac-dev
9mac-dev

Posted on

Java 17 Features Every Senior Developer Should Know - Part 2: Records

Part 2 of 6 - Essential Java 17+ features for senior developers upgrading from Java 8, 11, or 13.

Records solve the data class problem: automatically generating constructors, equals(), hashCode(), and toString(). This article covers immutable data carriers, validation patterns, and real-world applications from DTOs to domain models.


Why This Matters

For 20+ years, creating a simple data class meant writing the same ceremony over and over: private final fields, constructor, getters, equals(), hashCode(), and toString(). The most common data structure in Java required 30-50 lines of boilerplate.

Other languages solved this decades ago. Scala case classes (2003), Kotlin data classes (2016), even C# record types (2020)—all understood that data carriers should be one line, not thirty.

Java 17 finally brought a native solution: Records.


What are Records?

A record is a special class designed to be an immutable carrier for data. When you declare a record, the compiler automatically generates constructors, accessors, equals(), hashCode(), and toString().

Before Java 16:

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) { /* 10 lines */ }
    @Override public int hashCode() { return Objects.hash(x, y); }
    @Override public String toString() { return "Point[x=" + x + ", y=" + y + "]"; }
}
Enter fullscreen mode Exit fullscreen mode

After Java 16:

public record Point(int x, int y) {}
Enter fullscreen mode Exit fullscreen mode

That's it. The compiler generates everything.


Simple Example

public record Book(String title, String author, int year) {}

public class RecordExample {
    public static void main(String[] args) {
        var book = new Book("Clean Code", "Robert Martin", 2008);

        System.out.println(book);  // Book[title=Clean Code, author=Robert Martin, year=2008]
        System.out.println("Title: " + book.title());

        var book2 = new Book("Clean Code", "Robert Martin", 2008);
        System.out.println("Equal: " + book.equals(book2));  // true
    }
}
Enter fullscreen mode Exit fullscreen mode

Key Characteristics

  • Immutable - Fields are private final, no setters
  • No inheritance - Records are implicitly final
  • Perfect for - DTOs, value objects, domain entities

Key Insight

Records eliminate boilerplate without sacrificing type safety. They're the native Java solution to what Project Lombok tried to solve. Your code expresses intent: "immutable data carrier."


More Advanced Examples

Generic Records

public record Pair<T>(T first, T second) {}

public class GenericRecordExample {
    public static void main(String[] args) {
        var stringPair = new Pair<>("Alice", "Bob");
        var numberPair = new Pair<>(42, 100);

        System.out.println(stringPair);     // Pair[first=Alice, second=Bob]
        System.out.println(numberPair);     // Pair[first=42, second=100]
    }
}
Enter fullscreen mode Exit fullscreen mode

Records with Custom Constructor (Compact Constructor)

public record Person(String name, int age) {
    // Compact constructor - implicit parameter assignment
    public Person {
        if (age < 0) {
            throw new IllegalArgumentException("Age cannot be negative");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Records vs POJOs

Feature Traditional Class Record
Lines of code 30-50 1-2
Constructor Manual Generated
equals/hashCode Manual Generated
toString Manual Generated
Immutability Optional Guaranteed
Inheritance Flexible Limited (final)

Real-World Use Cases

  1. DTOs (Data Transfer Objects) - Perfect for API responses
  2. Value Objects - Immutable domain model objects
  3. Configuration Objects - Simplify config classes
  4. Test Fixtures - Clean test data builders
  5. Tuple-like Types - Type-safe alternatives to Map

Read the Full Article

Discover much more in Part 2: Records on blog.9mac.dev:

  • Generic records with type parameters
  • Custom constructors and validation
  • Sealed record hierarchies
  • 10+ real-world examples
  • Performance comparison with POJOs
  • Integration with streams and collections
  • Serialization considerations

GitHub Repository

All code examples are ready to clone and run:

git clone https://github.com/dawid-swist/blog-9mac-dev-code.git
cd blog-post-examples/java/2025-10-25-java17-features-every-senior-developer-should-know
../../gradlew test
Enter fullscreen mode Exit fullscreen mode

Next in the series: Part 3 - Sealed Classes (controlled inheritance hierarchies)

Top comments (0)