HuntExams Academy logo
JavaIntermediate#fundamentals#java14

What are records in Java (Java 14+)?

Answer

Records are immutable data carrier classes that automatically generate constructors, accessors, equals(), hashCode(), and toString() based on their declared components, reducing boilerplate for DTOs.

Example

record Point(int x, int y) {}
Point p = new Point(1, 2);
p.x(); // 1
System.out.println(p); // Point[x=1, y=2]

Detailed explanation

Records are a special kind of Java class for representing data with minimal boilerplate. They were introduced as a preview feature in Java 14, remained in preview in Java 15, and became permanent in Java 16. A declaration such as `record Point(int x, int y) {}` defines two record components. You can create an instance with `Point p = new Point(1, 2);` and read its values using `p.x()` and `p.y()`. Records are useful for DTOs, query results, and small value objects where the declared data should define the object's identity.

For each component, the compiler provides a private final field and a public accessor named exactly after the component, rather than a JavaBean getter such as `getX()`. It also supplies a canonical constructor whose parameters match the components, plus `equals()`, `hashCode()`, and `toString()` implementations unless explicitly declared. Generated equality requires the same record type and equal component values; generated hashing uses the components consistently with that equality. For this example, printing `p` produces `Point[x=1, y=2]`. Every record implicitly extends `java.lang.Record` and is final, so it cannot extend another class or be subclassed, although it can implement interfaces.

The important qualification is that records are shallowly immutable, not necessarily deeply immutable. Their component fields cannot be reassigned after construction, but a component can reference a mutable object. For example, a record containing a `List<String>` can expose a list whose contents change. A defensive copy such as `names = List.copyOf(names);` inside a compact constructor prevents structural changes through that list, though mutable elements would still require separate handling. A compact constructor omits the parameter list and lets you validate or normalise component parameters before the compiler assigns them to fields.

Records may declare methods, static fields, and additional constructors, but cannot add non-static instance fields beyond their components. Additional constructors must delegate to another constructor, ultimately reaching the canonical constructor. Interviewers usually expect you to explain the generated members, distinguish shallow immutability from deep immutability, and recognise when a transparent data carrier is preferable to a regular class with mutable state or hidden representation.

Key points to remember

  • Records were preview features in Java 14 and 15, and became permanent in Java 16.
  • The compiler provides component fields, accessors, a canonical constructor, equals(), hashCode(), and toString().
  • Accessors use component names: x(), not getX(); records do not generate setters.
  • Records are final, extend java.lang.Record, and can implement interfaces.
  • Component references are final, but referenced objects may remain mutable.

Common mistakes candidates make

  • Calling records deeply immutable without considering mutable components and defensive copying.
  • Saying records were a permanent feature in Java 14 or assuming accessors follow JavaBean getter naming.
  • Assuming records can extend arbitrary classes or declare extra non-static instance fields.

Likely follow-up questions

How do you validate record components?

Use a compact constructor, for example: `record Age(int value) { Age { if (value < 0) throw new IllegalArgumentException("Negative age"); } }`. The compiler assigns the component fields after the constructor body completes normally.

Can you customise the generated methods?

Yes, you can explicitly declare accessors, equals(), hashCode(), and toString(). Any custom equality and hashing must remain consistent, and custom accessors should preserve the record's data-carrier semantics.

Do records compare array components by content?

No, generated equality uses an array component's reference-based equality, not Arrays.equals(). Two records containing distinct arrays with identical elements therefore need not be equal.

Interview tip: Start with the Point example, list the generated members, and then highlight shallow immutability and the Java 16 finalisation.

Related Interview Questions

1
JavaAdvanced#fundamentals#java17

What is the difference between sealed classes and final classes (Java 17)?

Open
2
JavaIntermediate#fundamentals#java14

What is the difference between switch statement and switch expression (Java 14+)?

Open
3
JavaBeginner#strings#java15

What is text block syntax in Java (Java 15+)?

Open