What members can you add to a Java record, and what is forbidden?
answer
- Components = the only instance fields
- Static OK, extra instance fields forbidden
- Can override accessors/equals/hashCode/toString
- Implicitly final, extends Record, implements interfaces only
basics
~20 sYou can add instance methods, static fields, static methods, and nested types. You can also override the auto-generated accessors, equals, hashCode, and toString. You cannot declare extra instance fields beyond the ones in the record header.
solid answer
~40 sA record's state is fixed by its header (the components in the parentheses), which become the only instance fields. You may add: instance methods, static fields and static methods, and nested types. You may override any generated member - the per-component accessors, equals, hashCode, toString, and even add an explicit canonical or compact constructor for validation. What you cannot do is declare additional instance fields, because that would mean state outside the components and break the record's promise that the components fully describe the object. Records are implicitly final, extend java.lang.Record, and cannot extend another class, but they can implement interfaces. So records are a transparent carrier for immutable data, but still allow behavior and static helpers.
code
java · 17 linesrecord Point(int x, int y) implements Comparable<Point> {
static final Point ORIGIN = new Point(0, 0); // static field: OK
// private int z; // ERROR: extra instance field forbidden
int distanceSquared() { // instance method: OK
return x * x + y * y;
}
static Point of(int x, int y) { // static method: OK
return new Point(x, y);
}
@Override public int compareTo(Point o) {
return Integer.compare(distanceSquared(), o.distanceSquared());
}
}go deeper
Knows you can add methods and statics but not extra instance fields, and that records are final.
Explains the components-are-the-state rule and that records extend java.lang.Record so they can only implement interfaces.
Articulates the transparency contract: forbidding hidden state guarantees equals/toString reflect exactly the components; uses overrides (e.g. defensive accessor) deliberately.
Frames records within nominal-typing and data-carrier design, weighs them against Lombok/POJOs and sealed hierarchies, and reasons about API/serialization implications of the constraints.
### What a record is A **record** (Java 16+, preview in 14-15) is a special kind of class for holding immutable data. You declare it with a *header* listing its **components**: ```java record Point(int x, int y) {} ``` Here `x` and `y` are the components. The compiler automatically generates: a `private final` field for each component, a **canonical constructor** taking all components, a **public accessor** per component (named `x()`, `y()` - no `get` prefix), and value-based `equals`, `hashCode`, and `toString`. ### The state-description rule The core constraint is: **the components ARE the state.** Everything the record stores must be declared in the header. This is why you **cannot declare additional instance fields**: ```java record Point(int x, int y) { private int z; // COMPILE ERROR: field declaration not allowed } ``` This rule keeps records *transparently* carrying their data - `toString`, `equals`, and the accessors all reflect exactly the components and nothing hidden. ### What you CAN add - **Instance methods** - ordinary behavior is fine: ```java record Point(int x, int y) { int distanceSquared() { return x*x + y*y; } } ``` - **Static fields and static methods** - these belong to the class, not an instance, so they don't count as per-object state: ```java record Point(int x, int y) { static final Point ORIGIN = new Point(0, 0); static Point of(int x, int y) { return new Point(x, y); } } ``` - **Overriding generated members** - you can replace any generated accessor, `equals`, `hashCode`, or `toString`, and supply an explicit canonical constructor or a **compact constructor** for validation: ```java record Range(int lo, int hi) { Range { // compact constructor - validates if (lo > hi) throw new IllegalArgumentException(); } } ``` - **Nested types** - you can nest classes, interfaces, or other records inside. ### Structural constraints - A record is **implicitly `final`** - you cannot subclass it. - It **implicitly extends `java.lang.Record`**, so it **cannot extend any other class** (Java has single inheritance, and the slot is taken). - It **can implement interfaces**, which is the main way to share behavior across records. ### Why this matters Records trade flexibility for clarity: by forbidding hidden state and subclassing, they guarantee that two records are equal iff their components are equal, and that the object is a faithful, immutable snapshot of its data. You still get methods and statics, so a record is not just a 'dumb struct' - it can carry behavior.
- Why are static fields allowed but extra instance fields are not?Static fields belong to the class, not to any instance, so they are not part of an object's state. Instance fields would be hidden per-object state outside the components, breaking the record's transparency guarantee (that components fully describe the object).
- Can a record override its auto-generated accessor?Yes. You can declare `public int x() { ... }` to replace the generated one - for example to return a defensive copy of a mutable component - as long as it returns the matching type.
saying these in an interview costs you the question
- Thinking you can add a non-static instance field to a record
- Believing records cannot have methods at all (they can)
- Confusing 'cannot extend a class' with 'cannot implement an interface'